diff options
Diffstat (limited to 'macros/luatex/latex/luacas/tex/test/parser.lua')
-rw-r--r-- | macros/luatex/latex/luacas/tex/test/parser.lua | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/macros/luatex/latex/luacas/tex/test/parser.lua b/macros/luatex/latex/luacas/tex/test/parser.lua new file mode 100644 index 0000000000..6cb22a1bdb --- /dev/null +++ b/macros/luatex/latex/luacas/tex/test/parser.lua @@ -0,0 +1,323 @@ +-- Rudimentary parser for making the CAS easier to use. Essentially just wraps SymbolExpression() around symbols and Integer() around integers. + + + +require("calculus._init") + +-- Splits a string on a seperator. +function split(str, sep) + local t={} + for match in string.gmatch(str, "([^".. sep .."]+)") do + t[#t+1] = match + end + return t +end + +-- Displays an expression. For use in the parser. +function disp(expression, inline, simple) + if type(expression) ~= "table" then + tex.print(tostring(expression)) + elseif expression.autosimplify then + if inline then + if simple then + tex.print('$' .. expression:autosimplify():tolatex() .. '$') + else + tex.print('$' .. expression:tolatex() .. '$') + end + else + if simple then + tex.print('\\[' .. expression:autosimplify():tolatex() .. '\\]') + else + tex.print('\\[' .. expression:tolatex() .. '\\]') + end + end + else + tex.print(tostring(expression)) + end +end + +-- Displays an expression. For use in the parser. +function displua(expression) + if type(expression) ~= "table" then + print(tostring(expression)) + elseif expression.autosimplify then + print(expression:autosimplify():tolatex()) + else + print(tostring(expression)) + end +end + +function vars(...) + for _, string in ipairs(table.pack(...)) do + if string ~= "_" then + _G[string] = SymbolExpression(string) + end + end +end + +function clearvars() + for index, value in pairs(_G) do + if type(value) == "table" and value.type and value:type() == SymbolExpression then + _G[index] = nil + end + end +end + +function range(a, b, step) + if not b then + b = a + a = Integer.one() + end + step = step or Integer.one() + local f = + step > Integer.zero() and + function(_, lastvalue) + local nextvalue = lastvalue + step + if nextvalue <= b then return nextvalue end + end or + step < Integer.zero() and + function(_, lastvalue) + local nextvalue = lastvalue + step + if nextvalue >= b then return nextvalue end + end or + function(_, lastvalue) return lastvalue end + return f, nil, a - step + end + +function factor(exp,squarefrei) + if exp:type() == Integer then + return exp:primefactorization() + end + if exp:type() == PolynomialRing then + if not squarefrei then + return exp:factor() + else + if exp.ring == Integer.getring() or Rational.getring() then + return exp:squarefreefactorization() + end + if exp.ring == IntegerModN.getring() then + return exp:modularsquarefreefactorization() + end + return exp:factor() + end + end + return exp:autosimplify():factor() +end + +function expand(exp) + return exp:autosimplify():expand() +end + +function simplify(exp) + return exp:simplify() +end + +function exp(x) + return e^x +end + +function substitute(tbl,expr) + return expr:substitute(tbl) +end + +function roots(expression) + poly,ispoly = topoly(expression) + if ispoly then + return poly:roots() + end + return RootExpression(expression) +end + +function combine(expr) + return expr:combine() +end + +function Mod(f,n) + if f:type() == Integer then + return IntegerModN(f,n) + end + if f:type() == PolynomialRing and f.ring == Integer.getring() then + local coeffs = {} + for i=0,f.degree:asnumber() do + coeffs[i] = IntegerModN(f.coefficients[i],n) + end + return PolynomialRing(coeffs,f.symbol,f.degree) + end +end + +function Poly(coefficients,symbol,degree) + local variable = symbol or 'x' + return PolynomialRing:new(coefficients,variable,degree) +end + +function topoly(a) + a = a:expand():autosimplify() + return a:topolynomial() +end + +function gcd(a,b) + if a:type() == Integer and b:type() == Integer then + return Integer.gcd(a,b) + end + if a:type() == PolynomialRing and b:type() == PolynomialRing then + return PolynomialRing.gcd(a,b) + end +end + +function gcdext(a,b) + if a:type() == Integer and b:type() == Integer then + return Integer.extendedgcd(a,b) + end + A, ATF = topoly(a) + B, BTF = topoly(b) + if ATF and BTF then + return PolynomialRing.extendedgcd(A,B) + end + return nil,nil,nil +end + +function parfrac(f,g,ffactor) + local f,check1 = topoly(f) + local g,check2 = topoly(g) + if check1 and check2 then + if f.degree >= g.degree then + local q,r + q,r = f:divremainder(g) + return q + PolynomialRing.partialfractions(r,g,ffactor) + else + return PolynomialRing.partialfractions(f,g,ffactor) + end + else + return f/g + end +end + +function factorial(a) + return FactorialExpression(a) +end + +-- Constants for the CAS. We may not want these in Lua itself, but in the latex end the user probably expects them. +e = E +pi = PI +-- sqrt = SQRT +ln = LN +log = LOG +int = INT +sin = SIN +cos = COS +tan = TAN +csc = CSC +sec = SEC +cot = COT +arcsin = ARCSIN +arccos = ARCCOS +arctan = ARCTAN +arccsc = ARCCSC +arcsec = ARCSEC +arccot = ARCCOT +abs = ABS + +function ZTable(t) + t = t or {} + return setmetatable(t, JoinTables(getmetatable(t), + {__index = function (t, k) + if type(k) == "table" and k.type and k:type() == Integer then + return rawget(t, k:asnumber()) + else + return rawget(t, k) + end + end, + __newindex = function (t, k, v) + if type(k) == "table" and k.type and k:type() == Integer then + rawset(t, k:asnumber(), v) + else + rawset(t, k, v) + end + end})) +end + +function RR(n) + if type(n) == "number" then + return n + end + + if type(n) == "string" then + return tonumber(n) + end + + if type(n) == "table" and n.asnumber then + return n:asnumber() + end + + error("Could not convert to a real number.") +end + +function ZZ(n) + if type(n) == "table" and n.type and n:type() == Rational then + return n.numerator // n.denominator + end + return Integer(n) +end + +function QQ(n) + if type(n) == "table" then + return n + end + + if type(n) == "number" then + n = tostring(n) + end + + if type(n) == "string" then + local parts = split(n, "%.") + if #parts == 1 then + return Integer(parts[1]) + else + return Integer(parts[1])..Integer(parts[2]) + end + end + + error("Could not convert to a rational number.") +end + +--- Parses raw input into Lua code and executes it. +--- @param input string +function CASparse(input) + + -- First, we replace any occurance of a number with an integer or rational version of itself. + local str = string.gsub(input, ".?[0-9]+", function (s) + -- Here, we are part of an identifier, so we don't replace anything + if string.match(string.sub(s, 1, 1), "[A-Z]") or string.match(string.sub(s, 1, 1), "[a-z]") or string.match(string.sub(s, 1, 1), "_") then + return + end + + if string.match(string.sub(s, 1, 1), "[0-9]") then + return "Integer('" .. s .. "')" + end + + return string.sub(s, 1, 1) .. "Integer('" .. string.sub(s, 2, #s) .. "')" + end) + + -------------------------- + -- HERE COMES THE JANK. -- + -------------------------- + + -- Replaces each instance of a decimal with .., so we can use integer metatables to convert it into a rational properly. + str = string.gsub(str, "Integer%('[0-9]+'%)%.Integer%('[0-9]+'%)", function (s) + local ints = split(s, "%.") + return ints[1] .. ".." .. ints[2] + end) + str = string.gsub(str, ".?%.Integer%('[0-9]+'%)", function (s) + if string.sub(s, 1, 2) == ".." then + return + end + return string.sub(s, 1, 1) .. "Integer('0')." .. string.sub(s, 2, #s) + end) + + local exe, err = load(str .. "\n return true") + if exe then + exe() + else + print(err) + end +end
\ No newline at end of file |