7 Commits

Author SHA1 Message Date
bakpakin
cbf71d2cb2 Add rockspec for version to 1.3-1 2016-03-05 16:42:14 -05:00
bakpakin
93a19d93c8 Wrap error throwing functions in pcalls and make doc look cleaner for tiny.filter. 2016-03-05 16:35:06 -05:00
bakpakin
4148855233 Bump version. 2016-03-05 16:20:09 -05:00
bakpakin
27f881444a Fix string filter generator. 2016-03-05 16:06:56 -05:00
bakpakin
3b4bd9920b Add generic filter. 2016-03-05 15:34:21 -05:00
bakpakin
206361ca38 Make filters compile. Add some filter tests. 2016-03-05 13:34:44 -05:00
bakpakin
29158282bd Update Copyright Date. 2016-02-29 19:19:34 -05:00
4 changed files with 167 additions and 67 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2015 Calvin Rose Copyright (c) 2016 Calvin Rose
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.3-1"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.3-1"
}
description = {
summary = "Entity Component System for Lua.",
detailed = [[
Pure Lua implementation of an easy to use, compact, fast, and flexible
Entity Component System. Works well with Object Orientation.
]],
homepage = "https://github.com/bakpakin/tiny-ecs",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
tiny = "tiny.lua"
}
}
+43
View File
@@ -90,6 +90,49 @@ describe('tiny-ecs:', function()
end) end)
it("Can use functions as subfilters", function()
local f1 = tiny.requireAny('a', 'b', 'c')
local f2 = tiny.requireAll('x', 'y', 'z')
local f = tiny.requireAll(f1, f2)
assert.truthy(f(nil, {
x = true, y = true, z = true, a = true, b = true, c = true
}))
assert.truthy(f(nil, {
x = true, y = true, z = true, a = true
}))
assert.falsy(f(nil, {
x = true, y = true, a = true
}))
assert.falsy(f(nil, {
x = true, y = true, z = true
}))
end)
it("Can use string filters", function()
local f = tiny.filter('a|b|c')
assert.truthy(f(nil, {
a = true, b = true, c = true
}))
assert.truthy(f(nil, {
a = true
}))
assert.truthy(f(nil, {
b = true
}))
assert.truthy(f(nil, {
c = true
}))
assert.falsy(f(nil, {
x = true, y = true, z = true
}))
end)
end) end)
describe('World:', function() describe('World:', function()
+98 -65
View File
@@ -1,5 +1,5 @@
--[[ --[[
Copyright (c) 2015 Calvin Rose Copyright (c) 2016 Calvin Rose
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in
@@ -23,7 +23,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- @author Calvin Rose -- @author Calvin Rose
-- @license MIT -- @license MIT
-- @copyright 2015 -- @copyright 2015
local tiny = { _VERSION = "1.2-1" } local tiny = { _VERSION = "1.3-1" }
-- Local versions of standard lua functions -- Local versions of standard lua functions
local tinsert = table.insert local tinsert = table.insert
@@ -85,88 +85,121 @@ local tiny_remove
-- --
-- @section Filter -- @section Filter
-- A helper function to compile filters.
local filterJoin
-- A helper function to filters from string
local filterBuildString
do
local loadstring = loadstring or load
local function getchr(c)
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, ...)
local accum = {}
local build = {}
for i = 1, select('#', ...) do
local item = select(i, ...)
if type(item) == 'string' then
accum[#accum + 1] = ("(e[%s] ~= nil)"):format(make_safe(item))
elseif type(item) == 'function' then
build[#build + 1] = ('local subfilter_%d_ = select(%d, ...)'):format(i, i)
accum[#accum + 1] = ('(subfilter_%d_(system, e))'):format(i)
else
error 'Filter token must be a string or a filter function.'
end
end
local source = ('do %s\n return function(system, e) return %s(%s) end end'):format(
table.concat(build, '\n'),
prefix,
table.concat(accum, seperator)
)
local loader, err = loadstring(source)
if err then error(err) end
return loader(...)
end
function filterJoin(...)
local state, value = pcall(filterJoinRaw, ...)
if state then return value else return nil, value end
end
local function buildPart(str)
local accum = {}
local subParts = {}
str = str:gsub('%b()', function(p)
subParts[#subParts + 1] = buildPart(p:sub(2, -2))
return ('\255%d'):format(#subParts)
end)
for invert, part, sep in str:gmatch('(%!?)([^%|%&%!]+)([%|%&%!]?)') do
if part:match('^\255%d+$') then
local partIndex = tonumber(part:match(part:sub(2)))
accum[#accum + 1] = ('%s(%s)'):format(invert == '' and '' or 'not', subParts[partIndex])
else
accum[#accum + 1] = ("(e[%s] %s nil)"):format(make_safe(part), invert == '' and '~=' or '==')
end
if sep ~= '' then
accum[#accum + 1] = (sep == '|' and ' or ' or ' and ')
end
end
return table.concat(accum)
end
function filterBuildString(str)
local source = ("return function(_, e) return %s end"):format(buildPart(str))
local loader, err = loadstring(source)
if err then
error(err)
end
return loader()
end
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(...)
local components = {...} return filterJoin('', ' and ', ...)
local len = #components
return function(system, e)
local c
for i = 1, len do
c = components[i]
if type(c) == 'function' then
if not c(system, e) then
return false
end
elseif e[c] == nil then
return false
end
end
return true
end
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(...)
local components = {...} return filterJoin('', ' or ', ...)
local len = #components
return function(system, e)
local c
for i = 1, len do
c = components[i]
if type(c) == 'function' then
if c(system, e) then
return true
end
elseif e[c] ~= nil then
return true
end
end
return false
end
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(...)
local components = {...} return filterJoin('not', ' and ', ...)
local len = #components
return function(system, e)
local c
for i = 1, len do
c = components[i]
if type(c) == 'function' then
if not c(system, e) then
return true
end
elseif e[c] == nil then
return true
end
end
return false
end
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(...)
local components = {...} return filterJoin('not', ' or ', ...)
local len = #components
return function(system, e)
local c
for i = 1, len do
c = components[i]
if type(c) == 'function' then
if c(system, e) then
return false
end
elseif e[c] ~= nil then
return false
end
end
return true
end end
--- Makes a Filter from a string. Syntax of `pattern` is as follows.
--
-- * Tokens are alphanumeric strings including underscores.
-- * Tokens can be separated by |, &, or surrounded by parentheses.
-- * Tokens can be prefixed with !, and are then operated on with a boolean 'not'.
--
-- Examples are best:
-- 'a|b|c' - Matches entities with an 'a' component OR a 'b' component or a 'c' component.
-- 'a&!b&c' - Matches entities with an 'a' component AND NOT a 'b' component AND a 'c' component.
-- 'a|(b&c&d)|e - Matches 'a' OR ('b' AND 'c' AND 'd') OR 'e'
-- @param pattern
function tiny.filter(pattern)
local state, value = pcall(filterBuildString, pattern)
if state then return value else return nil, value end
end end
--- System functions. --- System functions.