diff options
Diffstat (limited to 'macros/luatex/latex/luacas/tex/luacas_init.lua')
-rw-r--r-- | macros/luatex/latex/luacas/tex/luacas_init.lua | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/macros/luatex/latex/luacas/tex/luacas_init.lua b/macros/luatex/latex/luacas/tex/luacas_init.lua new file mode 100644 index 0000000000..8d58e259e7 --- /dev/null +++ b/macros/luatex/latex/luacas/tex/luacas_init.lua @@ -0,0 +1,304 @@ +--- @class LuaCAS +--- @class Module +--- A table that initalizes and stores luacas modules. +--- @field moduleinfo table<string, table> +--- @field core Module +--- @field algebra Module +--- @field calculus Module +local luacas = {} + + +-- a table containing a list of module names, descriptions, class names, variables, required files, and dependent modules +luacas.moduleinfo = { + ["core"]= + { + "Classes needed for creating basic expressions such as symbols, binary operations, and functions.", + { + "Expression", + "AtomicExpression", + "CompoundExpression", + "ConstantExpression", + "SymbolExpression", + "BinaryOperation", + "FunctionExpression", + + "__ExpressionOperations" + }, + {"E", "I", "PI"}, + { + "_lib.luacas-table", + "core.luacas-expression", + "core.luacas-atomicexpression", + "core.luacas-compoundexpression", + "core.luacas-constantexpression", + "core.luacas-symbolexpression", + "core.luacas-binaryoperation", + "core.luacas-functionexpression", + "core.binaryoperation.luacas-power", + "core.binaryoperation.luacas-product", + "core.binaryoperation.luacas-sum", + "core.binaryoperation.luacas-quotient", + "core.binaryoperation.luacas-difference", + }, + {} + }, + + ["algebra"]= + { + "Classes for elements of rings (integers, rationals, polynomials, etc., and operations on those elements (polynomial factoring). Also includes some elementary functions like logs and trig functions.", + { + "Ring", + "EuclideanDomain", + "Field", + "PolynomialRing", + "Integer", + "Rational", + "IntegerModN", + "SqrtExpression", + "AbsExpression", + "Equation", + "FactorialExpression", + "Logarithm", + "RootExpression", + "TrigExpression", + + "__RingOperations", -- Not classes, but we need to set the environment for this + "__EuclideanOperations", + "__FieldOperations", + "__PolynomialOperations", + "__IntegerOperations", + "__IntegerModNOperations", + "__RationalOperations" + }, + {"ABS", "ARCCOS", "ARCCOT", "ARCCSC", "ARCSEC", "ARCSIN", "ARCTAN", "COS", "COT", "CSC", "FACT", "LN", "LOG", "SEC", "SIN", "TAN"}, + { + "algebra.luacas-ring", + "algebra.luacas-euclideandomain", + "algebra.luacas-field", + "algebra.luacas-polynomialring", + "algebra.luacas-integer", + "algebra.luacas-rational", + "algebra.luacas-integerquotientring", + "algebra.luacas-sqrtexpression", + "algebra.luacas-absexpression", + "algebra.luacas-equation", + "algebra.luacas-factorialexpression", + "algebra.luacas-logarithm", + "algebra.luacas-rootexpression", + "algebra.luacas-trigexpression", + "algebra.polynomialring.luacas-berlekampfactoring", + "algebra.polynomialring.luacas-zassenhausfactoring", + "algebra.polynomialring.luacas-decomposition" + }, + {"core"} + }, + + ["calculus"]= + { + "Classes for symbolic differentation and integration.", + { + "DerivativeExpression", + "IntegralExpression", + "DiffExpression" + }, + {"DD", "INT"}, + { + "calculus.luacas-derivativeexpression", + "calculus.luacas-integralexpression", + "calculus.luacas-diffexpression" + }, + {"algebra"} + } +} + +--- Retrieves a short description for a module. +--- @param mod string +--- @return string +function luacas:moduledesc(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][1] + end + return "Module not found." +end + +--- Retrieves a list of class names included in a module. +--- @param mod string +--- @return table<number,string> +function luacas:moduleclasses(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][2] + end + return {} +end + +--- Retrieves a list of fields included in a module. +--- @param mod string +--- @return table<number,string> +function luacas:modulefields(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][3] + end + return {} +end + +--- Retrieves a list of files included in a module. +--- @param mod string +--- @return table<number,string> +function luacas:modulefiles(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][4] + end + return {} +end + +--- Retrieves a list of a module's immediate dependencies. +--- @param mod string +--- @return table<number,string> +function luacas:moduledependencies(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][5] + end + return {} +end + +--- Initalizes a LuaCAS module and returns it. +--- Since this creates temporary global variables, it should be treated as atomic to avoid collisions. +--- @param mod string +--- @return Module|nil +function luacas:initmodule(mod) + if not self.moduleinfo[mod] then + return nil + end + + -- TODO: This is a special case, since the algebra module is dependent on the core module and vice versa, + -- but our initialization scheme does not allow for circular dependencies. + if mod == "core" then + self:initmodule("algebra") + end + + -- Since other modules require globals from their dependencies, we only remove global variables after all modules have been initalized + local G = self:saveglobalstate() + self:_initmodulerec(mod) + self:restoreglobalstate(G) + + return self[mod] +end + +--- Recursive part of initalizing modules. +--- @param mod string +function luacas:_initmodulerec(mod) + + for _, dep in ipairs(self:moduledependencies(mod)) do + self:_initmodulerec(dep) + end + + if self[mod] then + -- Since globals from a dependency might be needed to initialize a module, we create globals even if a module has already been initalized. + for class, _ in self[mod] do + _G[class] = self[mod][class] + end + else + -- Creates globals needed for modules dependent on this one. + self:initglobalmodule(mod) + + -- Adds classes to module table. + self[mod] = {} + for _, class in ipairs(self:moduleclasses(mod)) do + self[mod][class] = _G[class] + end + + -- Sets the environment for each method of each class to include all dependent classes of this class. + self:setmoduleenvironment(mod) + -- TODO: This is a special case, since the algebra module is dependent on the core module and vice versa, + -- but our initialization scheme does not allow for circular dependencies. + if mod == "algebra" then + self:setmoduleenvironment("core") + end + + -- Adds fields to module tables + for _, class in ipairs(self:modulefields(mod)) do + self[mod][class] = _G[class] + end + end +end + +--- Sets the environment for each method of each class in a module to the current global environment, so it can keep using global variables when reset. +--- @param mod string +function luacas:setmoduleenvironment(mod) + local realenv = self:saveglobalstate() + for _, class in pairs(self[mod]) do + -- print(_) + for _, func in pairs(class) do + -- print(" ", _) + if type(func) == "function" then + --- Functions only have an _ENV upvalue if they have a global variable, so this should work + local _envloc = debug.findupvalue(func, "_ENV") + if _envloc then + debug.upvaluejoin(func, debug.findupvalue(func, "_ENV"), function () return realenv end, 1) + end + end + end + + -- local meta = getmetatable(class) + -- if meta then + -- for _, func in pairs(meta) do + -- print(" ", _) + -- if type(func) == "function" then + -- --- Functions only have an _ENV upvalue if they have a global variable, so this should work + -- local _envloc = debug.findupvalue(func, "_ENV") + -- if _envloc then + -- debug.upvaluejoin(func, debug.findupvalue(func, "_ENV"), function () return realenv end, 1) + -- end + -- end + -- end + -- end + end +end + +--- Saves the current global state as a table and returns it. +--- @return table +function luacas:saveglobalstate() + local G = {} + for key, value in pairs(_G) do + G[key] = value + end + return G +end + +--- Saves the current global state as a table and returns it. +--- @param G table +function luacas:restoreglobalstate(G) + for key, _ in pairs(_G) do + if key ~= "_G" and key ~= "pairs" then + _G[key] = nil + end + end + for key, value in pairs(G) do + _G[key] = value + end +end + +--- Initalizes a LuaCAS module and creates global variables for all classes in that module. +--- DOES NOT HANDLE DEPENDENCIES, so dependent modules must be globally initalized first. +--- @param mod string +--- @return Module|nil +function luacas:initglobalmodule(mod) + for _, filename in ipairs(self:modulefiles(mod)) do + require(filename) + end +end + +--- Why is Lua like this? +function debug.findupvalue(fn, search_name) + local i = 1 + while true do + local name, val = debug.getupvalue(fn, i) + if not name then break end + if name == search_name then + return i, val + end + i = i + 1 + end +end + +return luacas
\ No newline at end of file |