diff --git a/lib/src/state/lua_state_impl.dart b/lib/src/state/lua_state_impl.dart index 38be845..ef133be 100644 --- a/lib/src/state/lua_state_impl.dart +++ b/lib/src/state/lua_state_impl.dart @@ -34,10 +34,10 @@ class LuaStateImpl implements LuaState, LuaVM { LuaStack? _stack = LuaStack(); /// 注册表 - LuaTable? registry = LuaTable(0, 0); + LuaTable? registry = LuaTable(); LuaStateImpl() { - registry!.put(lua_ridx_globals, LuaTable(0, 0)); + registry!.put(lua_ridx_globals, LuaTable()); LuaStack stack = LuaStack(); stack.state = this; _pushLuaStack(stack); @@ -427,7 +427,7 @@ class LuaStateImpl implements LuaState, LuaVM { @override void createTable(int nArr, int nRec) { - _stack!.push(LuaTable(nArr, nRec)); + _stack!.push(LuaTable()); } @override diff --git a/lib/src/state/lua_table.dart b/lib/src/state/lua_table.dart index 28253ad..709a7aa 100644 --- a/lib/src/state/lua_table.dart +++ b/lib/src/state/lua_table.dart @@ -1,169 +1,69 @@ -import 'dart:collection'; - -import '../number/lua_number.dart'; +import 'package:lua_dardo/src/state/table/lua_table_array.dart'; +import 'package:lua_dardo/src/state/table/lua_table_keys.dart'; +import 'package:lua_dardo/src/state/table/lua_table_map.dart'; class LuaTable { /// 元表 LuaTable? metatable; - List? arr; - Map? map; - - // used by next() - Map? keys; - Object? lastKey; - late bool changed; - - LuaTable(int nArr, int nRec) { - if (nArr > 0) { - // arr = List(nArr); - arr = []; - } - if (nRec > 0) { - // map = Map(nRec); - map = HashMap(); - } - } - - bool hasMetafield(String fieldName) { - return metatable != null && metatable!.get(fieldName) != null; - } - int length() { - return arr == null ? 0 : arr!.length; - } - - Object? get(Object? key) { - key = floatToInteger(key); - - if (arr != null && key is int) { - int idx = key; - if (idx >= 1 && idx <= arr!.length) { - return arr![idx - 1]; - } - } + final LuaTableArray arr = LuaTableArray(); + final LuaTableMap map = LuaTableMap(); + final LuaTableKeys keys = LuaTableKeys(); - return map != null ? map![key] : null; - } + bool hasMetafield(String fieldName) => metatable?.get(fieldName) != null; void put(Object? key, Object? val) { if (key == null) { throw Exception("table index is nil!"); } + if (key is double && key.isNaN) { throw Exception("table index is NaN!"); } - key = floatToInteger(key); - if (key is int) { - int idx = key; - if (idx >= 1) { - if (arr == null) { - arr = []; - } - - int arrLen = arr!.length; - if (idx <= arrLen) { - arr![idx-1] = val; - if (idx == arrLen && val == null) { - shrinkArray(); - } - return; - } - if (idx == arrLen + 1) { - if (map != null) { - map!.remove(key); - } - if (val != null) { - arr!.add(val); - expandArray(); - } - return; - } - } - } - - if (val != null) { - if (map == null) { - map = HashMap(); - } - map![key] = val; + if (val == null) { + _remove(key); + } else if (arr.isArrayIndex(key)) { + arr[key] = val; + } else if (arr.isArrayIndex(key, forInsert: true)) { + arr[key] = val; + _expandArray(); } else { - if (map != null) { - map!.remove(key); - } + map[key] = val; } + + keys.update(arr.length, map.keys); } - Object? floatToInteger(Object? key) { - if (key is double) { - double f = key; - if (LuaNumber.isInteger(f)) { - return f.toInt(); - } + void _remove(Object key) { + if (arr.isArrayIndex(key)) { + arr.removeAt(key); + } else { + map.remove(key); } - return key; } - void shrinkArray() { - for (int i = arr!.length - 1; i >= 0; i--) { - if (arr![i] == null) { - arr!.removeAt(i); - } + void _expandArray() { + int key = arr.length + 1; + Object? value = map[key]; + while (value != null) { + arr[key] = value; + map.remove(key); + + key++; + value = map[key]; } } - void expandArray() { - if (map != null) { - for (int idx = arr!.length + 1; ; idx++) { - Object? val = map!.remove(idx); - if (val != null) { - arr!.add(val); - } else { - break; - } - } + Object? get(Object? key) { + if (key != null) { + return (arr.isArrayIndex(key)) ? arr[key] : map[key]; } + return null; } - Object? nextKey(Object? key) { - if (keys == null || (key == null && changed)) { - initKeys(); - changed = false; - } + Object? nextKey(Object? key) => keys.nextKey(key); - Object? nextKey = keys![key]; - if (nextKey == null && key != null && key != lastKey) { - throw Exception("invalid key to 'next'"); - } + int length() => arr.length; - return nextKey; - } - - void initKeys() { - if (keys == null) { - keys = HashMap(); - } else { - keys!.clear(); - } - Object? key = null; - if (arr != null) { - for (int i = 0; i < arr!.length; i++) { - if (arr![i] != null) { - int nextKey = i + 1; - keys![key] = nextKey; - key = nextKey; - } - } - } - if (map != null) { - for (Object? k in map!.keys) { - Object? v = map![k]; - if (v != null) { - keys![key] = k; - key = k; - } - } - } - lastKey = key; - } -} \ No newline at end of file +} diff --git a/lib/src/state/table/lua_table_array.dart b/lib/src/state/table/lua_table_array.dart new file mode 100644 index 0000000..5248f85 --- /dev/null +++ b/lib/src/state/table/lua_table_array.dart @@ -0,0 +1,49 @@ + +import 'package:lua_dardo/src/number/lua_number.dart'; + +class LuaTableArray { + + List? _arr; + + bool isArrayIndex(Object key, {bool forInsert = false}) { + int idx = _floatToIndex(key); + bool canInsert = forInsert && idx == length; + return idx >= 0 && (idx < length || canInsert); + } + + void operator []= (Object key, Object value) { + int idx = _floatToIndex(key); + if (idx < length) { + _initializedArray[idx] = value; + } else { + _initializedArray.add(value); + } + } + + Object operator [] (Object key) { + int idx = _floatToIndex(key); + return _initializedArray[idx]; + } + + void removeAt(Object key) { + int idx = _floatToIndex(key); + _initializedArray.removeAt(idx); + } + + int _floatToIndex(Object key) { + int intKey = (key is double && LuaNumber.isInteger(key)) + ? key.toInt() + : (key is int) ? key : -1; + return intKey -1; + } + + int get length => (_arr != null) ? _initializedArray.length : 0; + + List get _initializedArray { + if (_arr == null) { + _arr = []; + } + return _arr!; + } + +} diff --git a/lib/src/state/table/lua_table_keys.dart b/lib/src/state/table/lua_table_keys.dart new file mode 100644 index 0000000..9ad2b55 --- /dev/null +++ b/lib/src/state/table/lua_table_keys.dart @@ -0,0 +1,51 @@ + +class LuaTableKeys { + + final Map _nextKeys = {}; + + Object? lastKey; + + int _arrLength = 0; + Iterable _keys = []; + bool _changed = false; + + void update(int arrLength, Iterable keys) { + _arrLength = arrLength; + _keys = keys; + _changed = true; + } + + Object? nextKey(Object? key) { + if (_changed) { + regenerate(); + } + + Object? nextKey = _nextKeys[key]; + + if (nextKey == null && key != null && key != lastKey) { + throw Exception("invalid key to 'next'"); + } + + return nextKey; + } + + void regenerate() { + _nextKeys.clear(); + + Object? key = null; + + for (int i=1; i < _arrLength; i++) { + _nextKeys[key] = i; + key = i+1; + } + + for (Object nextKey in _keys) { + _nextKeys[key] = nextKey; + key = nextKey; + } + + lastKey = key; + _changed = false; + } + +} diff --git a/lib/src/state/table/lua_table_map.dart b/lib/src/state/table/lua_table_map.dart new file mode 100644 index 0000000..20aa11e --- /dev/null +++ b/lib/src/state/table/lua_table_map.dart @@ -0,0 +1,26 @@ + +class LuaTableMap { + + Map? _map; + + void operator []= (Object key, Object value) { + _initializedMap[key] = value; + } + + Object? operator [] (Object key) => _initializedMap[key]; + + void remove(Object key) => _initializedMap.remove(key); + + Iterable get keys => (_map != null) + ? _initializedMap.keys + : Iterable.empty(); + + Map get _initializedMap { + if (_map == null) { + _map = {}; + } + + return _map!; + } + +} diff --git a/test/stdlib/table_test.dart b/test/stdlib/table_test.dart index 7c6432f..3c90848 100644 --- a/test/stdlib/table_test.dart +++ b/test/stdlib/table_test.dart @@ -32,8 +32,48 @@ print(table.concat(a1, ",")) return true; } +bool testTableTraversing(){ + bool result = false; + + try{ + LuaState state = LuaState.newState(); + state.openLibs(); + state.loadString(r''' +local alphabet = {} +alphabet.a = 'alpha' +alphabet.b = 'beta' + +--trigger keys initializastion +local val = next(alphabet, 'b') + +alphabet.c = 'gamma' + +result = false; +for k, v in pairs(alphabet) do + print(k, v) + if (k == 'c' and v == 'gamma') + then + result = true; + end +end +'''); + state.pCall(0, 0, 1); + LuaType resultType = state.getGlobal("result"); + if (resultType == LuaType.luaBoolean) { + result = state.toBoolean(state.getTop()); + } + } catch(e,s){ + print('$e\n$s'); + } + + return result; +} + void main() { test('lua table standard library test', () { expect(testTable(), true); }); + test('\nlua traversing table test', () { + expect(testTableTraversing(), true); + }); }