Change implementation of require/reject API

Remove the dependency on load/loadstring for requireXXX/rejectXXX API functions
This commit is contained in:
Ian Sharkey 2023-03-10 21:24:23 -05:00
parent 821914795d
commit 32a2e99127
2 changed files with 114 additions and 37 deletions

View File

@ -6,3 +6,71 @@ local systemB = tiny.system()
world:addSystem(systemA) world:addSystem(systemA)
world:addSystem(systemB) world:addSystem(systemB)
world:setSystemIndex(systemA, 1) world:setSystemIndex(systemA, 1)
--- test requireXXX/rejectXXX functions
local filt1 = tiny.requireAll("prop1", "prop2")
assert(filt1(nil, { }) == false)
assert(filt1(nil, { prop1 = 1 }) == false)
assert(filt1(nil, { prop1 = 1, prop3 = 2}) == false)
assert(filt1(nil, { prop1 = 1, prop2 = 2 }) == true)
assert(filt1(nil, { prop2 = 1 }) == false)
assert(filt1(nil, { prop2 = 1, prop1 = 1, prop3 = 2 }) == true)
local filt2 = tiny.requireAny("prop1", "prop2")
assert(filt2(nil, { }) == false)
assert(filt2(nil, { prop1 = 1 }) == true)
assert(filt2(nil, { prop1 = 1, prop3 = 2}) == true)
assert(filt2(nil, { prop1 = 1, prop2 = 2 }) == true)
assert(filt2(nil, { prop2 = 1 }) == true)
assert(filt2(nil, { prop2 = 1, prop1 = 1, prop3 = 2 }) == true)
assert(filt2(nil, { prop4 = 1, prop5 = 1, prop6 = 2 }) == false)
local filt3 = tiny.rejectAll("prop1", "prop2")
assert(filt3(nil, { }) == true)
assert(filt3(nil, { prop1 = 1 }) == true)
assert(filt3(nil, { prop1 = 1, prop3 = 2}) == true)
assert(filt3(nil, { prop1 = 1, prop2 = 2 }) == false)
assert(filt3(nil, { prop2 = 1 }) == true)
assert(filt3(nil, { prop2 = 1, prop1 = 1, prop3 = 2 }) == false)
local filt4 = tiny.rejectAny("prop1", "prop2")
assert(filt4(nil, { }) == true)
assert(filt4(nil, { prop1 = 1 }) == false)
assert(filt4(nil, { prop1 = 1, prop3 = 2}) == false)
assert(filt4(nil, { prop1 = 1, prop2 = 2 }) == false)
assert(filt4(nil, { prop2 = 1 }) == false)
assert(filt4(nil, { prop2 = 1, prop1 = 1, prop3 = 2 }) == false)
assert(filt4(nil, { prop4 = 1, prop5 = 1, prop6 = 2 }) == true)
local filt5 = tiny.requireAll("prop3", filt2)
assert(filt5(nil, {}) == false)
assert(filt5(nil, {prop1 = 1}) == false)
assert(filt5(nil, {prop1 = 1, prop2 = 1, prop3 = 1}) == true)
assert(filt5(nil, {prop1 = 1, prop2 = 1, prop3 = 1, prop4 = 1}) == true)
assert(filt5(nil, {prop1 = 1, prop2 = 1, prop4 = 1}) == false)
local filt6 = tiny.requireAny("prop3", filt2)
assert(filt6(nil, {}) == false)
assert(filt6(nil, {prop1 = 1}) == true)
assert(filt6(nil, {prop1 = 1, prop2 = 1, prop3 = 1}) == true)
assert(filt6(nil, {prop1 = 1, prop2 = 1, prop3 = 1, prop4 = 1}) == true)
assert(filt6(nil, {prop1 = 1, prop2 = 1, prop4 = 1}) == true)
assert(filt6(nil, {prop5 = 1, prop4 = 1}) == false)

View File

@ -91,40 +91,49 @@ local filterJoin
-- A helper function to filters from string -- A helper function to filters from string
local filterBuildString local filterBuildString
do
local loadstring = loadstring or load local function filterJoinRaw(invert, joining_op, ...)
local function getchr(c) local _args = {...}
return "\\" .. c:byte()
end
local function make_safe(text)
return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr)
end
local function filterJoinRaw(prefix, seperator, ...) return function(system, e)
local accum = {} local acc
local build = {} local args = _args
for i = 1, select('#', ...) do if joining_op == 'or' then
local item = select(i, ...) acc = false
if type(item) == 'string' then for i = 1, #args do
accum[#accum + 1] = ("(e[%s] ~= nil)"):format(make_safe(item)) local v = args[i]
elseif type(item) == 'function' then if type(v) == "string" then
build[#build + 1] = ('local subfilter_%d_ = select(%d, ...)') acc = acc or (e[v] ~= nil)
:format(i, i) elseif type(v) == "function" then
accum[#accum + 1] = ('(subfilter_%d_(system, e))'):format(i) acc = acc or v(system, e)
else else
error 'Filter token must be a string or a filter function.' error 'Filter token must be a string or a filter function.'
end end
end end
local source = ('%s\nreturn function(system, e) return %s(%s) end') else
:format( acc = true
table.concat(build, '\n'), for i = 1, #args do
prefix, local v = args[i]
table.concat(accum, seperator)) if type(v) == "string" then
local loader, err = loadstring(source) acc = acc and (e[v] ~= nil)
if err then error(err) end elseif type(v) == "function" then
return loader(...) acc = acc and v(system, e)
else
error 'Filter token must be a string or a filter function.'
end end
end
end
-- computes a simple xor
if invert then
return not acc
else
return acc
end
end
end
do
function filterJoin(...) function filterJoin(...)
local state, value = pcall(filterJoinRaw, ...) local state, value = pcall(filterJoinRaw, ...)
@ -169,25 +178,25 @@ end
--- Makes a Filter that selects Entities with all specified Components and --- Makes a Filter that selects Entities with all specified Components and
-- Filters. -- Filters.
function tiny.requireAll(...) function tiny.requireAll(...)
return filterJoin('', ' and ', ...) return filterJoin(false, 'and', ...)
end end
--- Makes a Filter that selects Entities with at least one of the specified --- Makes a Filter that selects Entities with at least one of the specified
-- Components and Filters. -- Components and Filters.
function tiny.requireAny(...) function tiny.requireAny(...)
return filterJoin('', ' or ', ...) return filterJoin(false, 'or', ...)
end end
--- Makes a Filter that rejects Entities with all specified Components and --- Makes a Filter that rejects Entities with all specified Components and
-- Filters, and selects all other Entities. -- Filters, and selects all other Entities.
function tiny.rejectAll(...) function tiny.rejectAll(...)
return filterJoin('not', ' and ', ...) return filterJoin(true, 'and', ...)
end end
--- Makes a Filter that rejects Entities with at least one of the specified --- Makes a Filter that rejects Entities with at least one of the specified
-- Components and Filters, and selects all other Entities. -- Components and Filters, and selects all other Entities.
function tiny.rejectAny(...) function tiny.rejectAny(...)
return filterJoin('not', ' or ', ...) return filterJoin(true, 'or', ...)
end end
--- Makes a Filter from a string. Syntax of `pattern` is as follows. --- Makes a Filter from a string. Syntax of `pattern` is as follows.