From d2db4e91a3cd14b8f5b6b246645a2d4bebb3ede2 Mon Sep 17 00:00:00 2001 From: Kazhnuz Date: Sat, 17 Jul 2021 20:07:43 +0200 Subject: [PATCH] feat: add predicate system --- .../birb/classes/predicate/init.lua | 115 ++++++++++++++++++ .../birb/classes/predicate/simple.lua | 68 +++++++++++ .../birb/classes/predicate/utils.lua | 65 ++++++++++ 3 files changed, 248 insertions(+) create mode 100644 sonic-radiance.love/birb/classes/predicate/init.lua create mode 100644 sonic-radiance.love/birb/classes/predicate/simple.lua create mode 100644 sonic-radiance.love/birb/classes/predicate/utils.lua diff --git a/sonic-radiance.love/birb/classes/predicate/init.lua b/sonic-radiance.love/birb/classes/predicate/init.lua new file mode 100644 index 0000000..26abbbb --- /dev/null +++ b/sonic-radiance.love/birb/classes/predicate/init.lua @@ -0,0 +1,115 @@ +-- classes/predicates :: a predicate system, to be an intermediary between condition +-- solvers and complex conditions. You simply create a predicate with your data and the +-- solver to create a predicate, then use the Predicate:solve() API to get your response + +-- To create a Predicate, prefer the Predicate.createPredicate instead of Predicate:new() +-- As it'll allow you to dynamically create simple or complex predicate on the fly +-- while Predicate:new() will try to create a complex predicate + +--[[ + Copyright © 2021 Kazhnuz + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local Predicate = Object:extend() +local SimplePredicate = require "birb.classes.predicate.simple" + +function Predicate.createPredicate(data, solver, asker) + local predicate = nil + if (type(data) == "table") then + predicate = Predicate(data, solver, asker) + end + if (type(data) == "string") then + predicate = SimplePredicate(data, solver, asker) + end + + if (predicate ~= nil) then + return predicate + else + error("Predicate data aren't a table or a string : " .. tostring(data)) + end +end + +function Predicate:new(data ,solver, asker) + self.solver = solver + self.asker = asker + self.beginAt = 1 + self:setType(data) + self.notTag = false + self:setList(data) +end + +function Predicate:solve() + local predicateSolved + if (self.type == "or") then + predicateSolved = self:solveOr() + else + predicateSolved = self:solveAnd() + end + + if (self.notTag) then + predicateSolved = (predicateSolved == false) + end + + return predicateSolved +end + +-- INTERNAL FUNCTIONS +-- Handle the complex predicate + +function Predicate:setType(data) + self.type = "and" + if (type(data[1]) == "string") then + if (data[1] == "or") or (data[1] == "and") then + self.beginAt = 2 + self.type = data[1] + end + end +end + +function Predicate:setList(data) + self.list = {} + for i = self.beginAt, #data, 1 do + if (i == #data) and (data[i] == "not") then + self.notTag = true + else + table.insert(self.list, Predicate.createPredicate(data[i], self.solver, self.asker)) + end + end +end + +function Predicate:solveOr() + for i, predicate in ipairs(self.list) do + if (predicate:solve()) then + return true + end + end + return false +end + +function Predicate:solveAnd() + for i, predicate in ipairs(self.list) do + if (not predicate:solve()) then + return false + end + end + return true +end + +return Predicate \ No newline at end of file diff --git a/sonic-radiance.love/birb/classes/predicate/simple.lua b/sonic-radiance.love/birb/classes/predicate/simple.lua new file mode 100644 index 0000000..f342721 --- /dev/null +++ b/sonic-radiance.love/birb/classes/predicate/simple.lua @@ -0,0 +1,68 @@ +-- classes/simple :: the actual solver of the predicate system. It parse a condition +-- system, and pass the data to a solver function. If the last argument of a condition +-- is "not", it'll negate the whole condition. + +-- Each solver function will get as argument the list of conditions, the predicate and the +-- asker. The predicate contain an utility subclass with some function to handle easily +-- some stuff like converting truth tags or comparing numbers +-- An empty string will return true, a non-existing solver question an error, except if the solver have a +-- default function. + +--[[ + Copyright © 2021 Kazhnuz + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local SimplePredicate = Object:extend() +local SEPARATOR = ":" +local NOT = "not" +local DEFAULT = "default" + +SimplePredicate.utils = require "birb.classes.predicate.utils" + +function SimplePredicate:new(data, solver, asker) + self.solver = solver + self.asker = asker + self.data = data +end + +function SimplePredicate:solve() + if (utils.string.isEmpty(self.data)) then + return true + end + local conditionArgs = utils.string.split(self.data, SEPARATOR) + local solverFunction = self:getFunction(conditionArgs[1]) + local conditionFulfilled = solverFunction(conditionArgs, self, self.asker) + if (conditionArgs[#conditionArgs] == NOT) then + conditionFulfilled = (not conditionFulfilled) + end + return conditionFulfilled +end + +function SimplePredicate:getFunction(functionName) + if (self.solver[functionName] == nil) then + if (self.solver[DEFAULT] == nil) then + error("Function " .. functionName .. " doesn't exist in solver, and it doesn't have a 'default' function.") + end + return self.solver[DEFAULT] + end + return self.solver[functionName] +end + +return SimplePredicate \ No newline at end of file diff --git a/sonic-radiance.love/birb/classes/predicate/utils.lua b/sonic-radiance.love/birb/classes/predicate/utils.lua new file mode 100644 index 0000000..fefe6fa --- /dev/null +++ b/sonic-radiance.love/birb/classes/predicate/utils.lua @@ -0,0 +1,65 @@ +-- predicate/utils :: Simple utilities for predicates + +--[[ + Copyright © 2021 Kazhnuz + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local ConditionsUtils = {} + +function ConditionsUtils.testVariables(var1, testType, var2) + local var2 = tonumber(var2) + if (testType == "eq" and var1 == var2) then + return true + end + if (testType == "ne" and var1 ~= var2) then + return true + end + if (testType == "gt" and var1 > var2) then + return true + end + if (testType == "ge" and var1 >= var2) then + return true + end + if (testType == "lt" and var1 < var2) then + return true + end + if (testType == "le" and var1 <= var2) then + return true + end + + return false +end + +function ConditionsUtils.testBool(bool, boolType) + return (bool == (boolType == "V")) +end + +function ConditionsUtils.merge(cond) + local returnString = "" + for i, condElement in ipairs(cond) do + returnString = returnString .. condElement + if (i < #cond) then + returnString = returnString .. ":" + end + end + return returnString +end + +return ConditionsUtils; \ No newline at end of file