– This file contains container classes, i.e. Lists and Dicts, plus some extended string functionality local List, Dict, Undict, undictmt, dictmt {:insert,:remove,:concat} = table
as_nomsu = => if type(@) == ‘number’ return tostring(@) if mt = getmetatable(@) if asnomsu = mt.as_nomsu return asnomsu(@) return tostring(@)
as_lua = => if type(@) == ‘number’ return tostring(@) if mt = getmetatable(@) if aslua = mt.as_lua return aslua(@) return tostring(@)
nthtolast = (n)=> @[#@-n+1]
– List and Dict classes to provide basic equality/tostring functionality for the tables – used in Nomsu. This way, they retain a notion of whether they were originally lists or dicts.
listmt = __type: “a List” __eq: (other)=> unless type(other) == ‘table’ and getmetatable(other) == getmetatable(@) and #other == #@ return false for i,x in ipairs(@) return false unless x == other[i] return true – Could consider adding a __newindex to enforce list-ness, but would hurt performance _tostring: => “[“..concat([asnomsu(b) for b in *@], “, “)..”]” asnomsu: => “[“..concat([asnomsu(b) for b in *@], “, “)..”]” aslua: => “aList{”..concat([as_lua(b) for b in *@], “, “)..”}” __lt: (other)=> assert type(@) == ‘table’ and type(other) == ‘table’, “Incompatible types for comparison” for i=1,math.max(#@, #other) if not @[i] and other[i] then return true elseif @[i] and not other[i] then return false elseif @[i] < other[i] then return true elseif @[i] > other[i] then return false return false __le: (other)=> assert type(@) == ‘table’ and type(other) == ‘table’, “Incompatible types for comparison” for i=1,math.max(#@, #other) if not @[i] and other[i] then return true elseif @[i] and not other[i] then return false elseif @[i] < other[i] then return true elseif @[i] > other[i] then return false return true __add: (other)=> ret = List[x for x in *@] for x in *other insert(ret, x) return ret _index: add: insert, append: insert add1atindex: (t,x,i)-> insert(t,i,x) atindex1add: insert pop: remove, removelast: remove, remove_index: remove last: (=> @[#@]), first: (=> @[1]) 1sttolast:nthtolast, 1ndtolast:nthtolast 1rdtolast:nthtolast, 1thtolast:nthtolast joined: => table.concat([tostring(x) for x in *@]), joinedwith: (glue)=> table.concat([tostring(x) for x in *@], glue), has: (item)=> for x in *@ if x == item return true return false remove: (item)=> for i,x in ipairs @ if x == item remove(@, i) indexof: (item)=> for i,x in ipairs @ if x == item return i return nil from1to: (start, stop)=> n = #@ start = (n+1-start) if start < 0 stop = (n+1-stop) if stop < 0 return List[@[i] for i=start,stop] from: (start)=> @from1to(start, -1) upto: (stop)=> @from1to(1, stop) copy: => List[@[i] for i=1,#@] reverse: => n = #@ for i=1,math.floor(n/2) @[i], @[n-i+1] = @[n-i+1], @[i] reversed: => List[@[i] for i=#@,1,-1] sort: => table.sort(@) sortby: (fn)=> keys = setmetatable {}, _index: (k)=> key = fn(k) @[k] = key return key table.sort(@, (a,b)->keys[a] <= keys[b]) sorted: => c = @copy! c\sort! return c sortedby: (fn)=> c = @copy! c\sortby(fn) return c filterby: (keep)=> deleted = 0 for i=1,#@ unless keep(@[i]) deleted += 1 elseif deleted > 0 @[i-deleted] = @[i] for i=#@-deleted+1,#@ @[i] = nil
filtered_by: (keep)=>
c = @copy!
c\filter_by(keep)
return c
-- TODO: remove this safety check to get better performance?
__newindex: (k,v)=>
assert type(k) == 'number', "List indices must be numbers"
rawset(@, k, v)
listmt.index.as_lua = listmt.as_lua listmt.index.as_nomsu = listmt.as_nomsu
List = (t,…)-> local l if type(t) == ‘table’ l = setmetatable(t, listmt) elseif type(t) == ‘function’ l = setmetatable({}, listmt) add = (…)-> for i=1,select(‘#’,…) do l[#l+1] = select(i,…) t(add) elseif type(t) == ‘thread’ l = setmetatable({}, listmt) for val in coroutine.wrap(t) l[#l+1] = val else error(“Unsupported List type: “..type(t)) if select(1,…) for x in *List(…) l[#l+1] = x return l
compliments = setmetatable({}, {__mode:’k’}) undictmt = __type: “an Inverse Dict” __index: (k)=> not compliments[@][k] and true or nil __newindex: (k,v)=> if k compliments[@][k] = nil else compliments[@][k] = true __eq: (other)=> unless type(other) == ‘table’ and getmetatable(other) == getmetatable(@) return false return compliments[@] == compliments[other] __len: => math.huge tostring: => “~”..dictmt.tostring(compliments[@]) asnomsu: => “~”..dictmt.asnomsu(compliments[@]) aslua: => “~”..dictmt.as_lua(compliments[@]) band: (other)=> if getmetatable(other) == undictmt – ~{x,y} & ~{y,z} == ~{x,y,z} == ~({x,y} | {y,z}) Undict(dictmt.bor(compliments[@], compliments[other])) else – ~{x,y} & {y,z} == {z} == {y,z} & ~{x,y} dictmt.__band(other, @) bor: (other)=> if getmetatable(other) == undictmt – ~{x,y} | ~{y,z} == ~{y} = ~({x,y} & {y,z}) Undict(dictmt.band(compliments[@], compliments[other])) else – ~{x,y} | {y,z} == ~{z} = ~({y,z} & ~{x,y}) Undict{k,v for k,v in pairs(compliments[@]) when not other[k]} bxor: (other)=> if getmetatable(other) == undictmt – ~{x,y} ^ ~{y,z} == {x,z} = {x,y} ^ {y,z} dictmt.bxor(compliments[@], compliments[other]) else – ~{x,y} ^ {y,z} == ~{x} = ~({x,y} & ~{y,z}) Undict(dictmt.__band(other, @)) __bnot: => Dict{k,v for k,v in pairs(compliments[@])}
Undict = (d)-> u = setmetatable({}, undictmt) compliments[u] = Dict{k,true for k,v in pairs(d) when v} return u
dictmt = __type: “a Dict” __eq: (other)=> unless type(other) == ‘table’ and getmetatable(other) == getmetatable(@) return false for k,v in pairs(@) return false unless v == other[k] for k,v in pairs(other) return false unless v == @[k] return true __len: => n = 0 for _ in pairs(@) do n += 1 return n _tostring: => “{”..concat([v == true and “.”..asnomsu(k) or “.#{k} = #{v}” for k,v in pairs @], “, “)..”}” asnomsu: => “{”..concat([v == true and “.”..asnomsu(k) or “.#{asnomsu(k)} = #{asnomsu(v)}” for k,v in pairs @], “, “)..”}” aslua: => “aDict{”..concat([“[ #{aslua(k)}]= #{aslua(v)}” for k,v in pairs @], “, “)..”}” __inext: (key)=> nextkey, value = next(@, key) if nextkey != nil return nextkey, Dict{key:nextkey, value:value} __band: (other)=> Dict{k,v for k,v in pairs(@) when other[k]} bor: (other)=> if getmetatable(other) == undictmt return undictmt.bor(other, @) ret = Dict{k,v for k,v in pairs(@)} for k,v in pairs(other) if ret[k] == nil then ret[k] = v return ret bxor: (other)=> if getmetatable(other) == undictmt return undictmt.bxor(other, @) ret = Dict{k,v for k,v in pairs(@)} for k,v in pairs(other) if ret[k] == nil then ret[k] = v else ret[k] = nil return ret __bnot: Undict
__add: (other)=>
ret = Dict{k,v for k,v in pairs(@)}
for k,v in pairs(other)
if ret[k] == nil then ret[k] = v
else ret[k] += v
return ret
__sub: (other)=>
ret = Dict{k,v for k,v in pairs(@)}
for k,v in pairs(other)
if ret[k] == nil then ret[k] = -v
else ret[k] -= v
return ret
Dict = (t,more,…)-> local d if type(t) == ‘table’ d = setmetatable(t, dictmt) elseif type(t) == ‘function’ d = setmetatable({}, dictmt) add = (…)-> for i=1,select(‘#’,…) do d[select(i,…)] = true add1eq2 = (k, v)-> d[k] = v t(add, add1eq2) elseif type(t) == ‘thread’ d = setmetatable({}, dictmt) for k,v in coroutine.wrap(t) d[k] = v else error(“Unsupported Dict type: “..type(t)) if more for k,v in pairs(Dict(more,…)) d[k] = v return d
return {:List, :Dict}
