Skip to content

Latest commit

 

History

History
194 lines (168 loc) · 5.61 KB

File metadata and controls

194 lines (168 loc) · 5.61 KB

Collections

SharpForge supports List<T> and Dictionary<K,V> from SFLib.Collections. No other collection types are provided; build your own from these primitives if needed.

List<T>

using SFLib.Collections;

var list = new List<string>();
List<unit> units = new();
list.Add("a");
list.Add("b");
int count = list.Count;
string item = list[0];
list[1] = "c";
bool has = list.Contains("a");
int idx = list.IndexOf("a");
list.Insert(0, "z");
list.Remove("a");
int removed = list.RemoveAll(item => item == "gone");
list.RemoveAt(0);
list.Reverse();
list.Sort();
list.Sort((a, b) => a - b);
string[] arr = list.ToArray();
list.Clear();

Target-typed new() lowers through the same list and dictionary helpers as the explicit constructor form, so List<unit> units = new(); emits the same SF__.ListNew__({}) runtime shape as new List<unit>().

All List<T> operations lower to SF__.ListXxx__(list, ...) helper calls. Indexing is 0-based in C#; the transpiler adds 1 for all list index expressions. The core helpers emitted at runtime:

function SF__.ListNew__(items)
    local list = { items = {}, version = 0 }
    if items ~= nil then
        for i = 1, #items do
            list.items[i] = items[i]
        end
    end
    return list
end

function SF__.ListCount__(list)  return #list.items end
function SF__.ListGet__(list, index)  return list.items[index + 1] end
function SF__.ListSet__(list, index, value)  list.items[index + 1] = value; list.version = list.version + 1 end
function SF__.ListAdd__(list, value)  table.insert(list.items, value); list.version = list.version + 1 end
function SF__.ListRemoveAt__(list, index)  table.remove(list.items, index + 1); list.version = list.version + 1 end
function SF__.ListRemoveAll__(list, match)
    local removed = 0
    for i = #list.items, 1, -1 do
        if match(SF__.ListUnwrap__(list.items[i])) then
            table.remove(list.items, i)
            removed = removed + 1
        end
    end
    if removed > 0 then list.version = list.version + 1 end
    return removed
end
function SF__.ListClear__(list)  list.items = {}; list.version = list.version + 1 end

nil values in a list are wrapped internally using a sentinel (SF__.ListNil__) so that nil entries don't collapse the table. ListGet__ and ListSet__ unwrap/wrap transparently.

local list = SF__.ListNew__({})
SF__.ListAdd__(list, "a")
SF__.ListAdd__(list, "b")
local count = SF__.ListCount__(list)
local item = SF__.ListGet__(list, 1)
SF__.ListSet__(list, 2, "c")
local has = SF__.ListContains__(list, "a")
local idx = SF__.ListIndexOf__(list, "a")
SF__.ListInsert__(list, 1, "z")
SF__.ListRemove__(list, "a")
local removed = SF__.ListRemoveAll__(list, function(item) return item == "gone" end)
SF__.ListRemoveAt__(list, 1)
SF__.ListReverse__(list)
SF__.ListSort__(list)
SF__.ListSort__(list, function(a, b) return (a - b) end)

function SF__.ListSort__(list, comparison)
    local compare = comparison or function(a, b)
        if a < b then return -1 end
        if a > b then return 1 end
        return 0
    end
    local items = list.items
    for i = 2, #items do
        local value = items[i]
        local j = i - 1
        while j >= 1 and compare(SF__.ListUnwrap__(value), SF__.ListUnwrap__(items[j])) < 0 do
            items[j + 1] = items[j]
            j = j - 1
        end
        items[j + 1] = value
    end
    list.version = list.version + 1
    return list
end
local arr = SF__.ListToArray__(list)
SF__.ListClear__(list)

foreach over List<T>

foreach (var item in list)
{
    Use(item);
}
for i = 1, SF__.ListCount__(list) do
    local item = SF__.ListGet__(list, i)
    Use(item)
end

Dictionary<K,V>

using SFLib.Collections;

var dict = new Dictionary<string, int>();
dict.Add("key", 1);
dict["key"] = 2;
int val = dict["key"];
bool has = dict.ContainsKey("key");
dict.Remove("key");
int count = dict.Count;
dict.Clear();
local dict = SF__.DictNew__()
SF__.DictAdd__(dict, "key", 1)
SF__.DictSet__(dict, "key", 2)
local val = SF__.DictGet__(dict, "key")
local has = SF__.DictContainsKey__(dict, "key")
SF__.DictRemove__(dict, "key")
local count = SF__.DictCount__(dict)
SF__.DictClear__(dict)

The core helpers emitted at runtime:

function SF__.DictNew__()  return { data = {}, keys = {}, version = 0 } end
function SF__.DictCount__(dict)  return #dict.keys end
function SF__.DictGet__(dict, key)
    local value = dict.data[key]
    if value == SF__.DictNil__ then return nil end
    return value
end
function SF__.DictSet__(dict, key, value)
    if dict.data[key] == nil then table.insert(dict.keys, key) end
    dict.data[key] = value == nil and SF__.DictNil__ or value
    dict.version = dict.version + 1
end
function SF__.DictAdd__(dict, key, value)
    if dict.data[key] ~= nil then error("duplicate key") end
    table.insert(dict.keys, key)
    dict.data[key] = value == nil and SF__.DictNil__ or value
    dict.version = dict.version + 1
end
function SF__.DictContainsKey__(dict, key)  return dict.data[key] ~= nil end
function SF__.DictClear__(dict)  dict.data = {}; dict.keys = {}; dict.version = dict.version + 1 end

nil values are stored using a SF__.DictNil__ sentinel so that nil entries are distinct from missing keys.

foreach over Dictionary<K,V>

foreach (var kv in dict)
{
    Use(kv.Key, kv.Value);
}
for _, kv__ in ipairs(SF__.DictKeys__(dict)) do
    local kv__key = kv__
    local kv__val = SF__.DictGet__(dict, kv__)
    Use(kv__key, kv__val)
end

Struct Keys / Values

When K or V is a struct type, dictionaries use a linear key comparer. The helper functions adjust accordingly.