diff options
Diffstat (limited to 'Master/texmf-dist/tex/generic/pgf/libraries/luamath')
3 files changed, 1459 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgflibraryluamath.code.tex b/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgflibraryluamath.code.tex new file mode 100644 index 00000000000..b06695a87fa --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgflibraryluamath.code.tex @@ -0,0 +1,499 @@ +% Copyright 2011 by Christophe Jorssen and Mark Wibrow +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. +% +% $Id: pgflibraryluamath.code.tex,v 1.11 2012/03/06 16:06:09 cjorssen Exp $ +% +% !!! Warning: this library does not work with fpu!!! + +% ******************************************************************* +% Some luatex stuff. Should be put elsewhere (e.g. in +% pgfutil-luatex-engine). + +% We assume luatex version > 0.39: +% - \directlua <general text> will work +% - \directlua is the only luatex primitive that we can assume +% accessible without being prefixed by the format via +% tex.enableprimitives. +% Ideas taken from the ifluatex package (Heiko Oberdiek) +\let\pgfutil@ifluatex\iffalse +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname directlua\endcsname\relax +\else + \expandafter\let\csname pgfutil@ifluatex\expandafter\endcsname + \csname iftrue\endcsname +\fi + +\pgfutil@ifluatex + \let\pgfutil@directlua\directlua + \pgfutil@directlua{% + tex.enableprimitives('pgfutil@',{'luaescapestring'})} +\else + \def\pgfutil@directlua#1{} + \def\pgfutil@luaescapestring#1{} +\fi + +% End of luatex stuff +% ******************************************************************* + +% Loading part: based on fpu library + +\newif\ifpgfluamathcomputationactive +\newif\ifpgfluamathparseractive + +\def\pgfluamath@makecomputationactive{% + \ifpgfluamathcomputationactive + \else + \pgfluamath@checkuninstallcmd% + \pgfluamath@install% + \pgfluamathcomputationactivetrue + \fi} + +\def\pgfluamath@makecomputationinactive{% + \ifpgfluamathcomputationactive + \pgfluamath@uninstall% + \pgfluamathcomputationactivefalse + \fi} + +\def\pgfluamath@makeparseractive{% + \ifpgfluamathparseractive + \else + \let\pgfluamath@pgfmathparse\pgfmathparse + \let\pgfmathparse\pgfluamathparse + \pgfluamathparseractivetrue + \fi} + +\def\pgfluamath@makeparserinactive{% + \ifpgfluamathparseractive + \let\pgfmathparse\pgfluamath@pgfmathparse + \pgfluamathparseractivefalse + \fi} + +\pgfqkeys{/pgf}{% + % Enable lua-side computation of \pgfmathresult + % (will be deactivated after the current TeX group) + luamath/.is choice, + luamath/only computation/.code={% + \pgfutil@ifluatex + \pgfluamath@makecomputationactive + \pgfluamath@makeparserinactive + \else + \pgfmath@error{Sorry, you need the luaTeX engine to use the + luamath library}% + \fi}, + luamath/parser and computation/.code={% + \pgfutil@ifluatex + \pgfluamath@makecomputationactive + \pgfluamath@makeparseractive + \else + \pgfmath@error{Sorry, you need the luaTeX engine to use the + luamath library}% + \fi}, + luamath/off/.code={% + \pgfluamath@makecomputationinactive + \pgfluamath@makeparserinactive}, + luamath/.default=only computation} + +\def\pgfluamath@uninstall@appendcmd#1{% + \expandafter\gdef\expandafter\pgfluamath@uninstall\expandafter{% + \pgfluamath@uninstall #1}} + +% If the uninstall command is already assembled, it will skip the +% uninstall assemblation. +\def\pgfluamath@checkuninstallcmd{% + \pgfutil@ifundefined{pgfluamath@uninstall}{% + \pgfutil@directlua{% + pgfluamathfunctions = require("pgfluamath.functions") + pgfluamathparser = require("pgfluamath.parser")} + \global\let\pgfluamath@uninstall=\pgfutil@empty + }{% + % We already HAVE an uninstall command (prepared globally). + % So: don't waste time assembling one! + \def\pgfluamath@uninstall@appendcmd##1{}% + \def\pgfluamath@prepareuninstallcmd##1{}% + }% +} + +% This assembles an uninstall command globally ON FIRST USAGE. +% See \pgfmathfloat@plots@checkuninstallcmd +\def\pgfluamath@prepareuninstallcmd#1{% + \expandafter\global\expandafter\let + \csname pgfluamath@backup@\string#1\endcsname=#1% + \expandafter\gdef\expandafter\pgfluamath@uninstall\expandafter{% + \pgfluamath@uninstall + \expandafter\let\expandafter#1\csname pgfluamath@backup@\string#1\endcsname}% +} + +\def\pgfluamath@install@function#1=#2{% + \pgfluamath@prepareuninstallcmd{#1}% + \let#1=#2% +} + +\def\pgfluamath@install{% + \pgfluamath@install@function\pgfmathadd@=\pgfluamathadd@% + \pgfluamath@install@function\pgfmathsubtract@=\pgfluamathsubtract@% + \pgfluamath@install@function\pgfmathneg@=\pgfluamathneg@% + \pgfluamath@install@function\pgfmathmultiply@=\pgfluamathmultiply@% + \pgfluamath@install@function\pgfmathdivide@=\pgfluamathdivide@% + % \pgfluamath@install@function\pgfmathdiv@=\pgfluamathdiv@% + \pgfluamath@install@function\pgfmathfactorial@=\pgfluamathfactorial@% + \pgfluamath@install@function\pgfmathsqrt@=\pgfluamathsqrt@% + % \pgfluamath@install@function\pgfmathpow@=\pgfluamathpow@% + \pgfluamath@install@function\pgfmathe@=\pgfluamathe@% + \pgfluamath@install@function\pgfmathexp@=\pgfluamathexp@% + \pgfluamath@install@function\pgfmathln@=\pgfluamathln@% + \pgfluamath@install@function\pgfmathlogten@=\pgfluamathlogten@% + % \pgfluamath@install@function\pgfmathlogtwo@=\pgfluamathlogtwo@% + \pgfluamath@install@function\pgfmathabs@=\pgfluamathabs@% + \pgfluamath@install@function\pgfmathmod@=\pgfluamathmod@% + \pgfluamath@install@function\pgfmathMod@=\pgfluamathMod@% + \pgfluamath@install@function\pgfmathround@=\pgfluamathround@% + \pgfluamath@install@function\pgfmathfloor@=\pgfluamathfloor@% + \pgfluamath@install@function\pgfmathceil@=\pgfluamathceil@% + % \pgfluamath@install@function\pgfmathint@=\pgfluamathint@% + % \pgfluamath@install@function\pgfmathfrac@=\pgfluamathfrac@% + % \pgfluamath@install@function\pgfmathreal@=\pgfluamathreal@% + \pgfluamath@install@function\pgfmathgcd@=\pgfluamathgcd@% + \pgfluamath@install@function\pgfmathisprime@=\pgfluamathisprime@% + \pgfluamath@install@function\pgfmathpi@=\pgfluamathpi@% + \pgfluamath@install@function\pgfmathrad@=\pgfluamathrad@% + \pgfluamath@install@function\pgfmathdeg@=\pgfluamathdeg@% + \pgfluamath@install@function\pgfmathsin@=\pgfluamathsin@% + \pgfluamath@install@function\pgfmathcos@=\pgfluamathcos@% + \pgfluamath@install@function\pgfmathtan@=\pgfluamathtan@% + \pgfluamath@install@function\pgfmathsec@=\pgfluamathsec@% + \pgfluamath@install@function\pgfmathcosec@=\pgfluamathcosec@% + \pgfluamath@install@function\pgfmathcot@=\pgfluamathcot@% + \pgfluamath@install@function\pgfmathasin@=\pgfluamathasin@% + \pgfluamath@install@function\pgfmathacos@=\pgfluamathacos@% + \pgfluamath@install@function\pgfmathatan@=\pgfluamathatan@% + \pgfluamath@install@function\pgfmathatantwo@=\pgfluamathatantwo@% + \pgfluamath@install@function\pgfmathmax@=\pgfluamathmax@% + \pgfluamath@install@function\pgfmathmin@=\pgfluamathmin@% + % \pgfluamath@install@function\pgfmath@pi=\pgfluamathpi@% + % \pgfluamath@install@function\pgfmathpi=\pgfluamathpi@% + % \pgfluamath@install@function\pgfmathe@=\pgfluamathe@% + % \pgfluamath@install@function\pgfmathe=\pgfluamathe@% + % \pgfluamath@install@function\pgfmathlessthan@=\pgfluamathlessthan@% + % \pgfluamath@install@function\pgfmathless@=\pgfluamathlessthan@% + % \pgfluamath@install@function\pgfmathgreaterthan@=\pgfluamathgreaterthan@% + % \pgfluamath@install@function\pgfmathgreater@=\pgfluamathgreaterthan@% + % \pgfluamath@install@function\pgfmathpow@=\pgfluamathpow@ + \pgfluamath@install@function\pgfmathrand@=\pgfluamathrand@ + \pgfluamath@install@function\pgfmathrand=\pgfluamathrand@ + \pgfluamath@install@function\pgfmathrnd@=\pgfluamathrnd@ + \pgfluamath@install@function\pgfmathrnd=\pgfluamathrnd@ + % \pgfluamath@install@function\pgfmathtrue@=\pgfluamathtrue@ + % \pgfluamath@install@function\pgfmathfalse@=\pgfluamathfalse@ + % \pgfluamath@install@function\pgfmathnot@=\pgfluamathnot@ + % \pgfluamath@install@function\pgfmathhex@=\pgfluamathhex@ + % \pgfluamath@install@function\pgfmathHex@=\pgfluamathHex@ + % \pgfluamath@install@function\pgfmathoct@=\pgfluamathoct@ + % \pgfluamath@install@function\pgfmathbin@=\pgfluamathbin@ + % \pgfluamath@install@function\pgfmathand@=\pgfluamathand@ + % \pgfluamath@install@function\pgfmathor@=\pgfluamathor@ + % \pgfluamath@install@function\pgfmathfactorial@=\pgfluamathfactorial@ + % \pgfluamath@install@function\pgfmathveclen@=\pgfluamathveclen@ + % \pgfluamath@install@function\pgfmathcosh@=\pgfluamathcosh@ + % \pgfluamath@install@function\pgfmathsinh@=\pgfluamathsinh@ + % \pgfluamath@install@function\pgfmathtanh@=\pgfluamathtanh@ + % \pgfluamath@install@function@unimplemented{ceil}% + % \pgfluamath@install@function@unimplemented{frac}% + % \pgfluamath@install@function@unimplemented{log2}% + % \pgfluamath@install@function@unimplemented{log10}% + % \pgfluamath@install@function@unimplemented{equalto}% + % \pgfluamath@install@function@unimplemented{random}% + % \pgfluamath@install@function@unimplemented{setseed}% + % \pgfluamath@install@function@unimplemented{Mod}% + % \pgfluamath@install@function@unimplemented{real}% + % \pgfluamath@install@function@unimplemented{notequal}% + \pgfluamath@install@function\pgfmathreciprocal=\pgfluamathreciprocal% + \pgfluamath@install@function\pgfpointnormalised=\pgfluamathpointnormalised +} + +\def\pgfluamathgetresult#1{% + \edef\pgfmathresult{\pgfutil@directlua{tex.print(-1,#1)}}} + +\def\pgfluamathpi@{% + \pgfluamathgetresult{pgfluamathfunctions.pi()}} +\def\pgfluamathe@{% + \pgfluamathgetresult{pgfluamathfunctions.e()}} +\def\pgfluamathadd@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.add(#1,#2)}} +\def\pgfluamathsubtract@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.substract(#1,#2)}} +\def\pgfluamathneg@#1{% + \pgfluamathgetresult{pgfluamathfunctions.neg(#1)}} +\def\pgfluamathmultiply@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.multiply(#1,#2)}} +\def\pgfluamathdivide@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.divide(#1,#2)}} +\def\pgfluamathabs@#1{% + \pgfluamathgetresult{pgfluamathfunctions.abs(#1)}} +\def\pgfluamathround@#1{% + \pgfluamathgetresult{pgfluamathfunctions.round(#1)}} +\def\pgfluamathfloor@#1{% + \pgfluamathgetresult{pgfluamathfunctions.floor(#1)}} +\def\pgfluamathceil@#1{% + \pgfluamathgetresult{pgfluamathfunctions.ceil(#1)}} +\def\pgfluamathgcd@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.gcd(#1,#2)}} +\def\pgfluamathisprime@#1{% + \pgfluamathgetresult{pgfluamathfunctions.isprime(#1)}} +\def\pgfluamathmax@#1{% + \pgfluamathgetresult{% + math.max(pgfluamathfunctions.split_braces_to_explist("#1"))}} +\def\pgfluamathmin@#1{% + \pgfluamathgetresult{% + math.min(pgfluamathfunctions.split_braces_to_explist("#1"))}} +\def\pgfluamathsin@#1{% + \pgfluamathgetresult{pgfluamathfunctions.Sin(#1)}} +\def\pgfluamathcos@#1{% + \pgfluamathgetresult{pgfluamathfunctions.Cos(#1)}} +\def\pgfluamathtan@#1{% + \pgfluamathgetresult{pgfluamathfunctions.Tan(#1)}} +\def\pgfluamathmod@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.mod(#1,#2)}} +\def\pgfluamathMod@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.Mod(#1,#2)}} +\def\pgfluamathrad@#1{% + \pgfluamathgetresult{pgfluamathfunctions.rad(#1)}} +\def\pgfluamathdeg@#1{% + \pgfluamathgetresult{pgfluamathfunctions.deg(#1)}} +\def\pgfluamathatan@#1{% + \pgfluamathgetresult{pgfluamathfunctions.aTan(#1)}} +\def\pgfluamathatantwo@#1#2{% + \pgfluamathgetresult{pgfluamathfunctions.aTan2(#1,#2)}} +\def\pgfluamathasin@#1{% + \pgfluamathgetresult{pgfluamathfunctions.aSin(#1)}} +\def\pgfluamathacos@#1{% + \pgfluamathgetresult{pgfluamathfunctions.aCos(#1)}} +\def\pgfluamathcot@#1{% + \pgfluamathgetresult{1/pgfluamathfunctions.Tan(#1)}} +\def\pgfluamathsec@#1{% + \pgfluamathgetresult{1/pgfluamathfunctions.Cos(#1)}} +\def\pgfluamathcosec@#1{% + \pgfluamathgetresult{1/pgfluamathfunctions.Sin(#1)}} +\def\pgfluamathexp@#1{% + \pgfluamathgetresult{pgfluamathfunctions.exp(#1)}} +\def\pgfluamathln@#1{% + \pgfluamathgetresult{pgfluamathfunctions.log(#1)}} +\def\pgfluamathlogten@#1{% + \pgfluamathgetresult{pgfluamathfunctions.log10(#1)}} +\def\pgfluamathsqrt@#1{% + \pgfluamathgetresult{pgfluamathfunctions.sqrt(#1)}} +\def\pgfluamathrnd@{% + \pgfluamathgetresult{pgfluamathfunctions.rnd()}} +\def\pgfluamathrand@{% + \pgfluamathgetresult{pgfluamathfunctions.rand(-1,1)}} +\def\pgfluamathfactorial@#1{% + \pgfluamathgetresult{pgfluamathfunctions.factorial(#1)}} +\def\pgfluamathreciprocal#1{% + \pgfluamathgetresult{1/#1}} +% \pgfluamath@install@function\pgfmath@pi=\pgfluamathpi@% +% \pgfluamath@install@function\pgfmathpi=\pgfluamathpi@% +% \pgfluamath@install@function\pgfmathe@=\pgfluamathe@% +% \pgfluamath@install@function\pgfmathe=\pgfluamathe@% +% \pgfluamath@install@function\pgfmathlessthan@=\pgfluamathlessthan@% +% \pgfluamath@install@function\pgfmathless@=\pgfluamathlessthan@% +% \pgfluamath@install@function\pgfmathgreaterthan@=\pgfluamathgreaterthan@% +% \pgfluamath@install@function\pgfmathgreater@=\pgfluamathgreaterthan@% +% \pgfluamath@install@function\pgfmathpow@=\pgfluamathpow@ +% \pgfluamath@install@function\pgfmathrand@=\pgfluamathrand@ +% \pgfluamath@install@function\pgfmathrand=\pgfluamathrand@ +% \pgfluamath@install@function\pgfmathrnd@=\pgfluamathrnd@ +% \pgfluamath@install@function\pgfmathrnd=\pgfluamathrnd@ +% \pgfluamath@install@function\pgfmathtrue@=\pgfluamathtrue@ +% \pgfluamath@install@function\pgfmathfalse@=\pgfluamathfalse@ +% \pgfluamath@install@function\pgfmathnot@=\pgfluamathnot@ +% \pgfluamath@install@function\pgfmathhex@=\pgfluamathhex@ +% \pgfluamath@install@function\pgfmathHex@=\pgfluamathHex@ +% \pgfluamath@install@function\pgfmathoct@=\pgfluamathoct@ +% \pgfluamath@install@function\pgfmathbin@=\pgfluamathbin@ +% \pgfluamath@install@function\pgfmathand@=\pgfluamathand@ +% \pgfluamath@install@function\pgfmathor@=\pgfluamathor@ +% \pgfluamath@install@function\pgfmathfactorial@=\pgfluamathfactorial@ +% \pgfluamath@install@function\pgfmathveclen@=\pgfluamathveclen@ +% \pgfluamath@install@function\pgfmathcosh@=\pgfluamathcosh@ +% \pgfluamath@install@function\pgfmathsinh@=\pgfluamathsinh@ +% \pgfluamath@install@function\pgfmathtanh@=\pgfluamathtanh@ + +\def\pgfluamathpointnormalised#1{% + \pgf@process{#1}% + \pgfutil@directlua{% + pgfluamathfunctions.pointnormalised(% + \pgf@sys@tonumber{\pgf@x},\pgf@sys@tonumber{\pgf@y})}% + \ignorespaces} + +% Parser + +\newif\ifpgfluamathunitsdeclared + +\def\pgfluamathparse#1{% + \pgfluamathunitsdeclaredfalse + % The following "two passes" (TeX -> lua -> TeX -> lua -> TeX) is + % required for chadef'ed named boxes. The parser + % adds \number in front of the box name (e.g. \mybox -> + % \number\mybox). Then this is expanded to the chardef'ed number via + % \edef expansion. + \edef\pgfluamath@temp{% + \pgfutil@directlua{% + % Double quotes " are needed here. On the lua side, single quotes + % ' are used. Take care to don't mix them. + pgfluamathparser.parse("\pgfutil@luaescapestring{#1}") + tex.sprint(parsed_expression)}}% + % We can now feed back the evaluator. + \edef\pgfluamathresult{% + \pgfutil@directlua{% + pgfluamathparser.eval('\pgfluamath@temp') + if pgfluamathparser.result == nil then + tex.print('nil') + else + tex.print(pgfluamathparser.result) + end}}% + \csname pgfluamathunitsdeclared\pgfutil@directlua{% + if pgfluamathparser.units_declared == true then + tex.print('true') + else + tex.print('false') + end}\endcsname + \ifpgfluamathparseractive + \let\pgfmathresult\pgfluamathresult + \let\ifpgfmathunitsdeclared=\ifpgfluamathunitsdeclared + \fi} + +\endinput +% NEW: CJ (06 March 2012) Work in progress +\documentclass{minimal} + +\usepackage{pgfmath} + +\makeatletter +\let\pgfutil@directlua\directlua + +\directlua{dofile('pgfmathparser.lua')} + +\def\pgfluamathdeclarefunction#1#2#3{% + \pgfutil@directlua{% + pgfluamath.declare_new_function('#1',#2,#3)}} + +\pgfluamathdeclarefunction{add}{2}{% + function (a,b) return a + b end} + +\pgfluamathdeclarefunction{substract}{2}{% + function (a,b) return a - (b) end} + +\pgfluamathdeclarefunction{neg}{1}{% + function (a) return -(a) end} + +\pgfluamathdeclarefunction{multiply}{2}{% + function (a,b) return a * b end} + +\pgfluamathdeclarefunction{divide}{2}{% + function (a,b) return a / b end} + +\pgfluamathdeclarefunction{pow}{2}{% + function (a,b) return math.pow(a,b) end} + +\pgfluamathdeclarefunction{deg}{1}{% + function (a) return math.deg(a) end} + +\pgfluamathdeclarefunction{ifthenelse}{3}{% + function (a,b,c) if a == 1 then return b else return c end end} + +\pgfluamathdeclarefunction{equal}{2}{% + function (a,b) if a == b then return 1 else return 0 end end} + +\pgfluamathdeclarefunction{greater}{2}{% + function (a,b) if a > b then return 1 else return 0 end end} + +\pgfluamathdeclarefunction{less}{2}{% + function (a,b) if a < b then return 1 else return 0 end end} + +\pgfluamathdeclarefunction{notequal}{2}{% + function (a,b) if a < b or a > b then return 1 else return 0 end end} + +\pgfluamathdeclarefunction{notless}{2}{% + function (a,b) if a >= b then return 1 else return 0 end end} + +\pgfluamathdeclarefunction{notgreater}{2}{% + function (a,b) if a <= b then return 1 else return 0 end end} + +\pgfluamathdeclarefunction{andPGF}{2}{% + function (a,b) if (a < 0 or a > 0) and (b < 0 or b > 0) then return + 1 else return 0 end end} + +\pgfluamathdeclarefunction{orPGF}{2}{% + function (a,b) if (a < 0 or a > 0) or (b < 0 or b > 0) then return 1 + else return 0 end end} + +\pgfluamathdeclarefunction{modulo}{2}{% + % This is the definition of the lua % modulo operator + % The % operator cannot be used here (catcode issue) + function (a,b) return a - math.floor(a/b)*b end} + +\pgfluamathdeclarefunction{int}{1}{% + function (a) return a - pgfluamath.defined_functions.modulo.code(a,1) end} + +\pgfluamathdeclarefunction{frac}{1}{% + function (a) return pgfluamath.defined_functions.modulo.code(a,1) end} + +\pgfluamathdeclarefunction{factorial}{1}{% + function (a) + a = math.abs(pgfluamath.defined_functions.int.code(a)) + if a == 1 then + return 1 + else + return a * pgfluamath.defined_functions.factorial.code(a-1) + end + end} + +\pgfluamathdeclarefunction{sqrt}{1}{% + function (a) return math.sqrt(a) end} + + + +\def\pgfluamathparseandresult#1{% + \pgfutil@directlua{% + local s = pgfluamath.transform_math_expr('#1',pgfluamath.defined_functions_pattern) + texio.write_nl('pgfluamath: parsed expression "' .. s .. '"') + loadstring('tex.sprint(-1,' .. s .. ')')()}} + +\makeatother +\begin{document} + +$\pgfluamathparseandresult{1+2} = 3$ + +$\pgfluamathparseandresult{2*3} = 6$ + +$\pgfluamathparseandresult{1--1} = 2$ + +$\pgfluamathparseandresult{6/2} = 3$ + +$\pgfluamathparseandresult{2^2} = 4$ + +$\pgfluamathparseandresult{1>2} = 0$ + +$\pgfluamathparseandresult{2>1} = 1$ + +$\pgfluamathparseandresult{1!=1} = 0$ + +$\pgfluamathparseandresult{1?2:3} = 2$ + +$\pgfluamathparseandresult{1.0||0} = 1$ + +$\pgfluamathparseandresult{1&&0} = 0$ + +$\pgfluamathparseandresult{sqrt(2)} = \pgfmathparse{sqrt(2)}\pgfmathresult$ + +%\pgfluamathparseandresult{{1,{2,3-7}[1],4}[1]^2} + +\end{document} +% Local Variables: +% TeX-engine: luatex +% End:
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgfluamath.functions.lua b/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgfluamath.functions.lua new file mode 100644 index 00000000000..fa3c1872a58 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgfluamath.functions.lua @@ -0,0 +1,313 @@ +-- Copyright 2011 by Christophe Jorssen +-- +-- This file may be distributed and/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License. +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more details. +-- +-- $Id: pgfluamath.functions.lua,v 1.9 2013/07/25 10:39:34 tantau Exp $ +-- + +local pgfluamathfunctions = pgfluamathfunctions or {} + +local mathabs, mathacos, mathasin = math.abs, math.acos, math.asin +local mathatan, mathatan2, mathceil = math.atan, math.atan2, math.ceil +local mathcos, mathcosh, mathdeg = math.cos, math.cosh, math.deg +local mathexp, mathfloor, mathfmod = math.exp, math.floor, math.fmod +local mathfrexp, mathhuge, mathldexp = math.frexp, math.huge, math.ldexp +local mathlog, mathlog10, mathmax = math.log, math.log10, math.max +local mathmin, mathmodf, mathpi = math.min, math.modf, math.pi +local mathpow, mathrad, mathrandom = math.pow, math.rad, math.random +local mathrandomseed, mathsin = math.randomseed, math.sin +local mathsinh, mathsqrt, mathtanh = math.sinh, math.sqrt, math.tanh +local mathtan = math.tan + +function pgfluamathfunctions.add(x,y) + return x+y +end + +function pgfluamathfunctions.substract(x,y) + return x-y +end + +function pgfluamathfunctions.neg(x) + return -x +end + +function pgfluamathfunctions.multiply(x,y) + return x*y +end + +function pgfluamathfunctions.divide(x,y) + return x/y +end + +function pgfluamathfunctions.pow(x,y) + return mathpow(x,y) +end + +function pgfluamathfunctions.factorial(x) +-- TODO: x must be an integer + if x == 0 then + return 1 + else + return x * factorial(x-1) + end +end + +function pgfluamathfunctions.deg(x) + return mathdeg(x) +end + +function pgfluamathfunctions.ifthenelse(x,y,z) + if x~= 0 then + return y + else + return z + end +end + +function pgfluamathfunctions.equal(x,y) + if x == y then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.greater(x,y) + if x > y then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.less(x,y) + if x < y then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.notequal(x,y) + if x ~= y then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.notless(x,y) + if x >= y then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.notgreater(x,y) + if x <= y then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.andPGF(x,y) + if (x ~= 0) and (y ~= 0) then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.orPGF(x,y) + if (x ~= 0) or (y ~= 0) then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.notPGF(x) + if x == 0 then + return 1 + else + return 0 + end +end + +function pgfluamathfunctions.pi() + return mathpi +end + +function pgfluamathfunctions.e() + return mathexp(1) +end + +function pgfluamathfunctions.abs(x) + return mathabs(x) +end + +function pgfluamathfunctions.floor(x) + return mathfloor(x) +end + +function pgfluamathfunctions.ceil(x) + return mathceil(x) +end + +function pgfluamathfunctions.exp(x) + return mathexp(x) +end + +function pgfluamathfunctions.log(x) + return mathlog(x) +end + +function pgfluamathfunctions.log10(x) + return mathlog10(x) +end + +function pgfluamathfunctions.sqrt(x) + return mathsqrt(x) +end + +function pgfluamathfunctions.rnd() + return mathrandom() +end + +function pgfluamathfunctions.rand() + return mathrandom(-1,1) +end + +function pgfluamathfunctions.deg(x) + return mathdeg(x) +end + +function pgfluamathfunctions.rad(x) + return mathrad(x) +end + +function pgfluamathfunctions.round(x) + if x<0 then + return -mathceil(mathabs(x)) + else + return mathceil(x) + end +end + +function pgfluamathfunctions.gcd(a, b) + if b == 0 then + return a + else + return gcd(b, a%b) + end +end + +function pgfluamathfunctions.isprime(a) + local ifisprime = true + if a == 1 then + ifisprime = false + elseif a == 2 then + ifisprime = true +-- if a > 2 then + else + local i, imax = 2, mathceil(mathsqrt(a)) + 1 + while ifisprime and (i < imax) do + if gcd(a,i) ~= 1 then + ifisprime = false + end + i = i + 1 + end + end + if ifisprime then + return 1 + else + return 0 + end +end + + +function pgfluamathfunctions.split_braces_to_explist(s) + -- (Thanks to mpg and zappathustra from fctt) + -- Make unpack available whatever lua version is used + -- (unpack in lua 5.1 table.unpack in lua 5.2) + local unpack = table.unpack or unpack + local t = {} + for i in s:gmatch('%b{}') do + table.insert(t, tonumber(i:sub(2, -2))) + end + return unpack(t) +end + +function pgfluamathfunctions.split_braces_to_table(s) + local t = {} + for i in s:gmatch('%b{}') do + table.insert(t, tonumber(i:sub(2, -2))) + end + return t +end + +function pgfluamathfunctions.mod(x,y) + if x/y < 0 then + return -(mathabs(x)%mathabs(y)) + else + return mathabs(x)%mathabs(y) + end +end + +function pgfluamathfunctions.Mod(x,y) + return mathabs(x)%mathabs(y) +end + +function pgfluamathfunctions.Sin(x) + return mathsin(mathrad(x)) +end + +function pgfluamathfunctions.Cos(x) + return mathcos(mathrad(x)) +end + +function pgfluamathfunctions.Tan(x) + return mathtan(mathrad(x)) +end + +function pgfluamathfunctions.aSin(x) + return mathdeg(mathasin(x)) +end + +function pgfluamathfunctions.aCos(x) + return mathdeg(mathacos(x)) +end + +function pgfluamathfunctions.aTan(x) + return mathdeg(mathatan(x)) +end + +function pgfluamathfunctions.aTan2(y,x) + return mathdeg(mathatan2(y,x)) +end + +function pgfluamathfunctions.pointnormalised (pgfx, pgfy) + local pgfx_normalised, pgfy_normalised + if pgfx == 0. and pgfy == 0. then + -- Orginal pgf macro gives this result + tex.dimen['pgf@x'] = "0pt" + tex.dimen['pgf@y'] = "1pt" + else + pgfx_normalised = pgfx/math.sqrt(pgfx^2 + pgfy^2) + pgfx_normalised = pgfx_normalised - pgfx_normalised%0.00001 + pgfy_normalised = pgfy/math.sqrt(pgfx^2 + pgfy^2) + pgfy_normalised = pgfy_normalised - pgfy_normalised%0.00001 + tex.dimen['pgf@x'] = tostring(pgfx_normalised) .. "pt" + tex.dimen['pgf@y'] = tostring(pgfy_normalised) .. "pt" + end + return nil +end + +return pgfluamathfunctions
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgfluamath.parser.lua b/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgfluamath.parser.lua new file mode 100644 index 00000000000..b059e334358 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/libraries/luamath/pgfluamath.parser.lua @@ -0,0 +1,647 @@ +-- Copyright 2011 by Christophe Jorssen and Mark Wibrow +-- +-- This file may be distributed and/or modified +-- +-- 1. under the LaTeX Project Public License and/or +-- 2. under the GNU Public License. +-- +-- See the file doc/generic/pgf/licenses/LICENSE for more details. +-- +-- $Id: pgfluamath.parser.lua,v 1.14 2012/04/29 20:36:29 ludewich Exp $ + +local pgfluamathparser = pgfluamathparser or {} + +require("pgfluamath.functions") + +-- lpeg is always present in luatex +local lpeg = require("lpeg") + +local S, P, R = lpeg.S, lpeg.P, lpeg.R +local C, Cc, Ct = lpeg.C, lpeg.Cc, lpeg.Ct +local Cf, Cg, Cs = lpeg.Cf, lpeg.Cg, lpeg.Cs +local V = lpeg.V + +local space_pattern = S(" \n\r\t")^0 + +local exponent_pattern = S("eE") + +local one_digit_pattern = R("09") +local positive_integer_pattern = one_digit_pattern^1 +local integer_pattern = P("-")^-1 * positive_integer_pattern +-- Valid positive decimals are |xxx.xxx|, |.xxx| and |xxx.| +local positive_decimal_pattern = (one_digit_pattern^1 * P(".") * + one_digit_pattern^1) + + (P(".") * one_digit_pattern^1) + + (one_digit_pattern^1 * P(".")) +local decimal_pattern = P("-")^-1 * positive_decimal_pattern +local float_pattern = decimal_pattern * exponent_pattern * integer_pattern +local number_pattern = float_pattern + decimal_pattern + integer_pattern + +local at_pattern = P("@") + +local underscore_pattern = P("_") + +local lower_letter_pattern = R("az") +local upper_letter_pattern = R("AZ") +local letter_pattern = lower_letter_pattern + upper_letter_pattern +local alphanum_pattern = letter_pattern + one_digit_pattern +local alphanum__pattern = alphanum_pattern + underscore_pattern +local alpha__pattern = letter_pattern + underscore_pattern + +local openparen_pattern = P("(") +local closeparen_pattern = P(")") +local opencurlybrace_pattern = P("{") +local closecurlybrace_pattern = P("}") +local openbrace_pattern = P("[") +local closebrace_pattern = P("]") + +-- local string = P('"') * C((1 - P('"'))^0) * P('"') + +local orop_pattern = P("||") +local andop_pattern = P("&&") + +local neqop_pattern = P("!=") + +local then_pattern = P("?") +local else_pattern = P(":") +local factorial_pattern = P("!") +local not_mark_pattern = P("!") +local radians_pattern = P("r") + +local comma_pattern = P(",") + +--[[ +local grammar = P { + -- "E" stands for expression + "ternary_logical_E", + ternary_logical_E = Cf(V("logical_or_E") * + Cg( then_mark * V("logical_or_E") * else_mark * V("logical_or_E"))^0,evalternary); + logical_or_E = Cf(V("logical_and_E") * Cg(orop * V("logical_and_E"))^0,evalbinary); + logical_and_E = Cf(V("equality_E") * Cg(andop * V("equality_E"))^0,evalbinary); + equality_E = Cf(V("relational_E") * + Cg((eqop * V("relational_E")) + (neqop * V("relational_E")))^0,evalbinary); + relational_E = Cf(V("additive_E") * Cg((lessop * V("additive_E")) + + (greatop * V("additive_E")) + + (lesseqop * V("additive_E")) + + (greateqop * V("additive_E")))^0,evalbinary); + additive_E = Cf(V("multiplicative_E") * Cg((addop * V("multiplicative_E")) + + (subop * V("multiplicative_E")))^0,evalbinary); + multiplicative_E = Cf(V("power_E") * Cg((mulop * V("power_E")) + + divop * V("power_E"))^0,evalbinary); + power_E = Cf(V("postfix_unary_E") * Cg(powop * V("postfix_unary_E"))^0,evalbinary); + postfix_unary_E = Cf(V("prefix_unary_E") * Cg(radians + factorial)^0,evalpostfixunary); + prefix_unary_E = Cf(Cg(not_mark + negop)^0 * V("E"),evalprefixunary), + E = string + float + decimal + integer / tonumber + + (openparen * V("ternary_logical_E") * closeparen) + + (func * param_beg * V("ternary_logical_E") * + (comma * V("ternary_logical_E"))^0 * param_end); +} + +local parser = space_pattern * grammar * -1 +--]] + +-- NOTE: \pgfmathparse{pi/3.14} will fail giving pgfluamathfunctions.pi()3.14 +-- (the / is missing, probably gobbled by one of the captures of the grammar). + +pgfluamathparser.transform_operands = P({ + 'transform_operands'; + + one_char = lpeg.P(1), + lowercase = lpeg.R('az'), + uppercase = lpeg.R('AZ'), + numeric = lpeg.R('09'), + dot = lpeg.P('.'), + exponent = lpeg.S('eE'), + sign_prefix = lpeg.S('-+'), + begingroup = lpeg.P('('), + endgroup = lpeg.P(')'), + backslash = lpeg.P'\\', + at = lpeg.P'@', + + alphabetic = lpeg.V'lowercase' + lpeg.V'uppercase', + alphanumeric = lpeg.V'alphabetic' + lpeg.V'numeric', + + integer = lpeg.V'numeric'^1, + real = lpeg.V'numeric'^0 * lpeg.V'dot' * lpeg.V'numeric'^1, + scientific = (lpeg.V'real' + lpeg.V'integer') * lpeg.V'exponent' * lpeg.V'sign_prefix'^0 * lpeg.V'integer', + + number = lpeg.V'scientific' + lpeg.V'real' + lpeg.V'integer', + function_name = lpeg.V('alphabetic') * lpeg.V('alphanumeric')^0, + + tex_cs = lpeg.V'backslash' * (lpeg.V'alphanumeric' + lpeg.V'at')^1, + tex_box_dimension_primative = lpeg.V'backslash' * (lpeg.P'wd' + lpeg.P'ht' + lpeg.P'dp'), + tex_register_primative = lpeg.V'backslash' * (lpeg.P'count' + lpeg.P'dimen'), + + tex_primative = lpeg.V'tex_box_dimension_primative' + lpeg.V'tex_register_primative', + tex_macro = -lpeg.V'tex_primative' * lpeg.V'tex_cs', + + tex_unit = + lpeg.P('pt') + lpeg.P('mm') + lpeg.P('cm') + lpeg.P('in') + + lpeg.P('ex') + lpeg.P('em') + lpeg.P('bp') + lpeg.P('pc') + + lpeg.P('dd') + lpeg.P('cc') + lpeg.P('sp'), + + tex_register_named = lpeg.C(lpeg.V'tex_macro'), + tex_register_numbered = lpeg.C(lpeg.V'tex_register_primative') * lpeg.C(lpeg.V'integer'), + tex_register_basic = lpeg.Cs(lpeg.Ct(lpeg.V'tex_register_numbered' + lpeg.V'tex_register_named') / pgfluamathparser.process_tex_register), + + tex_multiplier = lpeg.Cs((lpeg.V'tex_register_basic' + lpeg.C(lpeg.V'number')) / pgfluamathparser.process_muliplier), + + tex_box_width = lpeg.Cs(lpeg.P('\\wd') / 'width'), + tex_box_height = lpeg.Cs(lpeg.P('\\ht') / 'height'), + tex_box_depth = lpeg.Cs(lpeg.P('\\dp') / 'depth'), + tex_box_dimensions = lpeg.V'tex_box_width' + lpeg.V'tex_box_height' + lpeg.V'tex_box_depth', + tex_box_named = lpeg.Cs(lpeg.Ct(lpeg.V'tex_box_dimensions' * lpeg.C(lpeg.V'tex_macro')) / pgfluamathparser.process_tex_box_named), + tex_box_numbered = lpeg.Cs(lpeg.Ct(lpeg.V'tex_box_dimensions' * lpeg.C(lpeg.V'number')) / pgfluamathparser.process_tex_box_numbered), + tex_box_basic = lpeg.V'tex_box_named' + lpeg.V'tex_box_numbered', + + tex_register = lpeg.Cs(lpeg.V'tex_multiplier' * lpeg.V'tex_register_basic') + lpeg.V'tex_register_basic', + tex_box = lpeg.Cs(lpeg.Cs(lpeg.V'tex_multiplier') * lpeg.V'tex_box_basic') + lpeg.V'tex_box_basic', + tex_dimension = lpeg.Cs(lpeg.V'number' * lpeg.V'tex_unit' / pgfluamathparser.process_tex_dimension), + + tex_operand = lpeg.Cs(lpeg.V'tex_dimension' + lpeg.V'tex_box' + lpeg.V'tex_register'), + + function_name = lpeg.V'alphabetic' * (lpeg.V'alphanumeric'^1), + function_operand = lpeg.Cs(lpeg.Ct(lpeg.C(lpeg.V'function_name') * lpeg.C(lpeg.V'one_char') + lpeg.C(lpeg.V'function_name')) / pgfluamathparser.process_function), + + -- order is (always) important! + operands = lpeg.V'tex_operand' + lpeg.V'number' + lpeg.V'function_operand', + + transform_operands = lpeg.Cs((lpeg.V'operands' + 1)^0) + }) + +-- Namespaces will be searched in the following order. +pgfluamathparser.function_namespaces = { + 'pgfluamathfunctions', 'math', '_G'} + +pgfluamathparser.units_declared = false + +function pgfluamathparser.get_tex_box(box, dimension) + -- assume get_tex_box is only called when a dimension is required. + pgfluamathparser.units_declared = true + if dimension == 'width' then + return tex.box[box].width / 65536 -- return in points. + elseif dimension == 'height' then + return tex.box[box].height / 65536 + else + return tex.box[box].depth / 65536 + end +end + +function pgfluamathparser.get_tex_register(register) + -- register is a string which could be a count or a dimen. + if pcall(tex.getcount, register) then + return tex.count[register] + elseif pcall(tex.getdimen, register) then + pgfluamathparser.units_declared = true + return tex.dimen[register] / 65536 -- return in points. + else + pgfluamathparser.error = 'I do not know the TeX register "' .. register '"' + return nil + end + +end + +function pgfluamathparser.get_tex_count(count) + -- count is expected to be a number + return tex.count[tonumber(count)] +end + +function pgfluamathparser.get_tex_dimen(dimen) + -- dimen is expected to be a number + pgfluamathparser.units_declared = true + return tex.dimen[tonumber(dimen)] / 65536 +end + +function pgfluamathparser.get_tex_sp(dimension) + -- dimension should be a string + pgfluamathparser.units_declared = true + return tex.sp(dimension) / 65536 +end + + + +-- transform named box specification +-- e.g., \wd\mybox -> pgfluamathparser.get_tex_box["mybox"].width +-- +function pgfluamathparser.process_tex_box_named(box) + return 'pgfluamathparser.get_tex_box(\\number' .. box[2] .. ', "' .. box[1] .. '")' +end + +-- transform numbered box specification +-- e.g., \wd0 -> pgfluamathparser.get_tex_box["0"].width +-- +function pgfluamathparser.process_tex_box_numbered(box) + return 'pgfluamathparser.get_tex_box("' .. box[2] .. '", "' .. box[1] .. '")' +end + +-- transform a register +-- e.g., \mycount -> pgfluamathparser.get_tex_register["mycount"] +-- \dimen12 -> pgfluamathparser.get_tex_dimen[12] +-- +function pgfluamathparser.process_tex_register(register) + if register[2] == nil then -- a named register + return 'pgfluamathparser.get_tex_register("' .. register[1]:sub(2, register[1]:len()) .. '")' + else -- a numbered register + return 'pgfluamathparser.get_tex_' .. register[1]:sub(2, register[1]:len()) .. '(' .. register[2] .. ')' + end +end + +-- transform a 'multiplier' +-- e.g., 0.5 -> 0.5* (when followed by a box/register) +-- +function pgfluamathparser.process_muliplier(multiplier) + return multiplier .. '*' +end + +-- transform an explicit dimension to points +-- e.g., 1cm -> pgfluamathparser.get_tex_sp("1cm") +-- +function pgfluamathparser.process_tex_dimension(dimension) + return 'pgfluamathparser.get_tex_sp("' .. dimension .. '")' +end + +-- Check the type and 'namespace' of a function F. +-- +-- If F cannot be found as a Lua function or a Lua number in the +-- namespaces in containted in pgfluamathparser.function_namespaces +-- then an error results. +-- +-- if F is a Lua function and isn't followd by () (a requirement of Lua) +-- then the parentheses are inserted. +-- +-- e.g., random -> random() (if random is a function in the _G namespace) +-- pi -> math.pi (if pi is a constant that is only in the math namespace) +-- +function pgfluamathparser.process_function(function_table) + local function_name = function_table[1] + local char = function_table[2] + if (char == nil) then + char = '' + end + for _, namespace in pairs(pgfluamathparser.function_namespaces) do + local function_type = assert(loadstring('return type(' .. namespace .. '.' .. function_name .. ')'))() + if function_type == 'function' then + if not (char == '(') then + char = '()' + end + return namespace .. '.' .. function_name .. char + elseif function_type == 'number' then + return namespace .. '.' .. function_name .. char + end + end + pgfluamathparser.error = 'I don\'t know the function or constant \'' .. function_name .. '\'' +end + + +pgfluamathparser.remove_spaces = lpeg.Cs((lpeg.S' \n\t' / '' + 1)^0) + + +function pgfluamathparser.evaluate(expression) + assert(loadstring('pgfluamathparser._result=' .. expression))() + pgfluamathparser.result = pgfluamathparser._result +end + +function pgfluamathparser.parse(expression) + pgfluamathparser.write_to_log("Parsing expression:" .. expression) + pgfluamathparser.units_declared = false + + pgfluamathparser.error = nil + pgfluamathparser.result = nil + + -- Remove spaces + expression = pgfluamathparser.remove_spaces:match(expression) + + parsed_expression = + pgfluamathparser.transform_operands:match(expression) + pgfluamathparser.write_to_log("Transformed expression:" + .. parsed_expression) +end + +function pgfluamathparser.eval(expression) + pgfluamathparser.write_to_log("Evaluating expression:" .. expression) + pcall(pgfluamathparser.evaluate, expression) + pgfluamathparser.write_to_log("Result:" .. pgfluamathparser.result) + if pgfluamathparser.result == nil then + pgfluamathparser.error = "Sorry, I could not evaluate '" .. expression .. "'" + return nil + else + return pgfluamathparser.result + end +end + +pgfluamathparser.trace = true + +function pgfluamathparser.write_to_log (s) + if pgfluamathparser.trace then + texio.write_nl(s) + end +end + +return pgfluamathparser + +--[[ NEW: 2012/02/21, CJ, work in progress +-- We need 3 parsers (or 2, 2 and 3 could be merged) +-- (1) To get the number associated with a (\chardef'ed) box (e.g. +-- \ht\mybox -> \ht26) +-- This is part of a TeX ->(\directlua == \edef) -> lua -> (lpeg parser 1) +-- -> TeX -> (\directlua == \edef) +-- (2) To transform math and functions operands +-- (3) To transform units, dimen and count register + +-- TODO: strings delimited by "..." +-- TODO: units. \pgfmathparse{12+1cm}\pgfmathresult and \the\dimexpr 12pt+1cm\relax have the same result. Note: \pgfmathparse{(12+1)cm} won't work in plain pgfmath. +-- TODO: Parser 3 + + +pgfluamath = pgfluamath or {} + +pgfluamath.debug = true + +local lpeg = require('lpeg') +local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V +local C, Cc, Cs = lpeg.C, lpeg.Cc, lpeg.Cs +local Cf, Cg, Ct = lpeg.Cf, lpeg.Cg, lpeg.Ct +local match = lpeg.match + +local space = S(' \n\t') + +local lowercase = R('az') +local uppercase = R('AZ') +local alphabetic = lowercase + uppercase + +local digit = R('09') + +local alphanumeric = alphabetic + digit + +local dot = P('.') +local exponent = S('eE') +local sign_prefix = S('+-') + +local integer = digit^1 +local float = (digit^1 * dot * digit^0) + (digit^0 * dot * digit^1) +local scientific = (float + integer) * exponent * sign_prefix^-1 * integer + +local number = scientific + float + integer + +local at = P('@') + +local backslash = P('\\') + +local box_width = P('\\wd') +local box_height = P('\\ht') +local box_depth = P('\\dp') +local box_dimension = box_width + box_height + box_depth + +local tex_cs = backslash * (alphabetic + at)^1 + +local lparen = P('(') +local rparen = P(')') + +local lbrace = P('{') +local rbrace = P('}') + +local lbracket = P('[') +local rbracket = P(']') + +local thenop = P('?') +local elseop = P(':') + +local orop = P('||') +local andop = P('&&') + +local eqop = P('==') +local neqop = P('!=') + +local greaterop = P('>') +local lessop = P('<') +local greatereqop = P('>=') +local lesseqop = P('<=') + +local addop = P('+') +local subop = P('-') + +local mulop = P('*') +local divop = P('/') + +local powop = P('^') + +local radop = P('r') + +local notop = P('!') +local negop = P('-') + +local comma = P(',') + +function pgfluamath.namedbox_to_numberedbox (s) + -- Transforms '{\wd|\ht|\dp}\mybox' to '{\wd|\ht|\dp}\number\mybox' + local function transform (capture) + print('Captured ' .. capture) + return '\\number' .. capture + end + local dimension_of_a_named_box = + box_dimension * space^0 * (tex_cs / transform) + -- P(1) matches exactly one character + -- V(1) is the entry of the grammar with index 1. It defines the initial rule. + -- The manual says: + -- "If that entry is a string, it is assumed to be the name of the initial rule. + -- Otherwise, LPeg assumes that the entry 1 itself is the initial rule." + local grammar = + P({ + [1] = (dimension_of_a_named_box + P(1)) * V(1) + P(true) + }) + return print(match(Cs(grammar),s)) +end +--[[ +namedbox_to_numberedbox('test') +namedbox_to_numberedbox('\\test') +namedbox_to_numberedbox('\\wd\\test') +namedbox_to_numberedbox('\\wd\\test0') +namedbox_to_numberedbox('\\wd\\test01') +namedbox_to_numberedbox('\\wd\\test@t 012') +namedbox_to_numberedbox(' \\wd \\test012') +namedbox_to_numberedbox('\\wd\\test012\\test \\ht\\zozo0') +namedbox_to_numberedbox('\\wd0') +--]] + +-- Grammar (from lowest to highest precedence) +-- Need to adjust to the precedences in plain pgfmath parser +-- IfThenElse Expression +local ITE_E = V('ITE_E') +-- logical OR Expression +local OR_E = V('OR_E') +-- logical AND Expression +local AND_E = V('AND_E') +-- EQuality Expression +local EQ_E = V('EQ_E') +-- Relational EQuality Expression +local REQ_E = V('REQ_E') +-- ADDitive Expression +local ADD_E = V('ADD_E') +-- MULtiplicative Expression +local MUL_E = V('MUL_E') +-- POWer Expression +local POW_E = V('POW_E') +-- POSTfix Expression +local POST_E = V('POST_E') +-- PREfix Expression +local PRE_E = V('PRE_E') +-- ARRAY Expression +local ARRAY_E = V('ARRAY_E') +-- Expression +local E = V('E') + +function pgfluamath.transform_math_expr (s, f_patt) + if pgfluamath.debug then + texio.write_nl('pgfluamath: parsing expression "' .. s ..'"') + end + -- f_patt extends the grammar dynamically at the time of the call to the + -- parser. + -- The idea is to have a set of predefined acceptable functions (via e.g. + -- \pgfmathdeclarefunction) + if not f_patt then + -- P(false) is a pattern that always fails + -- (neutral element for the + operator on patterns) + f_patt = P(false) + end + local function transform_ITE(a, b, c) + if not b then + return a + else + return string.format('pgfluamath.defined_functions.ifthenelse.code(%s,%s,%s)', a, b, c) + end + end + local function transform_binary(a, b, c) + if not b then + return a + else + return string.format('pgfluamath.defined_functions.%s.code(%s,%s)', b, a, c) + end + end + local function transform_postunary(a, b) + if not b then + return a + else + return string.format('pgfluamath.defined_functions.%s.code(%s)', b, a) + end + end + local function transform_preunary(a, b) + if not b then + return a + else + return string.format('pgfluamath.defined_functions.%s.code(%s)', a, b) + end + end + local function transform_array(a, b) + -- One exception to the mimmic of plain pgfmath. I do not use the equivalent array function to transform the arrays because I don't know how to handle both cases {1,{2,3}[1],4}[1] and {1,{2,3},4}[1][1] with the parser and the array function. + -- So I convert a pgf array to a lua table. One can access one entry of the table like this ({1,2})[1] (note the parenthesis, ie {1,2}[1] won't work). + local s + if not b then + s = '{' + else + s = '({' + end + for i = 1,#a do + -- We change the index to fit with pgfmath plain convention (index starts at 0 while in lua index starts at 1) + s = s .. '[' .. tostring(i-1) .. ']=' .. a[i] + if i < #a then + s = s .. ',' + end + end + if b then + s = s .. '})' + for i = 1,#b do + s = s .. '[' .. b[i] .. ']' + end + else + s = s .. '}' + end + return s + end + local grammar = + lpeg.P({ + 'ITE_E', + ITE_E = (OR_E * (thenop * OR_E * elseop * OR_E)^-1) / transform_ITE, + OR_E = (AND_E * (orop * Cc('orPGF') * AND_E)^-1) / transform_binary, + AND_E = (EQ_E * (andop * Cc('andPGF') * EQ_E)^-1) / transform_binary, + EQ_E = (REQ_E * ((eqop * Cc('equal') + neqop * Cc('notequal')) * REQ_E)^-1) / transform_binary, + REQ_E = (ADD_E * ((lessop * Cc('less') + greaterop * Cc('greater') + lesseqop * Cc('notgreater') + greatereqop * Cc('notless')) * ADD_E)^-1) / transform_binary, + ADD_E = Cf(MUL_E * Cg((addop * Cc('add') + subop * Cc('substract')) * MUL_E)^0,transform_binary), + MUL_E = Cf(POW_E * Cg((mulop * Cc('multiply') + divop * Cc('divide')) * POW_E)^0,transform_binary), + POW_E = Cf(POST_E * Cg(powop * Cc('pow') * POST_E)^0,transform_binary), + POST_E = (PRE_E * (radop * Cc('rad'))^-1) / transform_postunary, + PRE_E = ((notop * Cc('notPGF') + negop * Cc('neg'))^-1 * E) / transform_preunary, + ARRAY_E = (lbrace * Ct(ITE_E * (comma * ITE_E)^0) * rbrace * Ct((lbracket * ITE_E * rbracket)^0)) / transform_array, + E = ((integer + float)^-1 * tex_cs^1) + f_patt + C(number) + (lparen * ITE_E * rparen) + ARRAY_E + lbrace * ITE_E * rbrace + }) + return lpeg.match(Cs(grammar),s) +end + +function pgfluamath.ptransform_math_expr(s, f_patt) + local st = pgfluamath.transform_math_expr(s, f_patt) + if st ~= nil then + return texio.write_nl(st) + end +end + +pgfluamath.defined_functions = {} +pgfluamath.defined_functions_pattern = P(false) + +function pgfluamath.declare_new_function (name, nargs, code) + -- nil is true + -- The function name CANNOT be a lua reserved word (so no 'and' nor 'or') + if pgfluamath.defined_functions[name] then + print('Function ' .. name .. ' is already defined. ' .. + 'I overwrite it!') + end + pgfluamath.defined_functions[name] = {['name'] = name, ['nargs'] = nargs, ['code'] = code} + + -- TODO + -- We need a function to dynamically (depending on the number of arguments) define a patten (in order to avoid the long ifcase-type structure). + local pattern + if nargs == 0 then + pattern = P(name) / function (s) return name .. '()' end + else if nargs == 1 then + pattern = ((P(name) * lparen * Cs(ITE_E) * rparen) / function (s) return 'pgfluamath.defined_functions.' .. name .. '.code(' .. s .. ')' end) + else if nargs == 2 then + pattern = (P(name) * lparen * Cs(ITE_E) * comma * Cs(ITE_E) * rparen / function (s1,s2) return 'pgfluamath.defined_functions.' .. name .. 'code(' .. s1 .. ',' .. s2 .. ')' end) + else if nargs == 3 then + pattern = (P(name) * lparen * Cs(ITE_E) * comma * Cs(ITE_E) * comma * Cs(ITE_E) * rparen / function (s1,s2,s3) return 'pgfluamath.defined_functions.' .. name .. 'code(' .. s1 .. ',' .. s2 .. ',' .. s3 .. ')' end) + end + end + end + end + -- TODO + -- This needs to be regenerated every time a new function is added, in conjunction with a sort of the table (see IMPORTANT below) + pgfluamath.defined_functions_pattern = pgfluamath.defined_functions_pattern + pattern +end + +-- IMPORTANT: for 'function' with *0* argument, the longest string, the first +-- ie declaring pi before pit won't work. +pgfluamath.declare_new_function('pit',0) +pgfluamath.declare_new_function('pi',0) +pgfluamath.declare_new_function('exp',1) +pgfluamath.declare_new_function('toto',1) +pgfluamath.declare_new_function('gauss',2) + +pgfluamath.ptransform_math_expr('exp(exp(1+1))',pgfluamath.defined_functions_pattern) + +pgfluamath.ptransform_math_expr('1?(2?3:4^5):gauss(6+toto(7),8+9>10?11:12))',pgfluamath.defined_functions_pattern) + +pgfluamath.ptransform_math_expr('!(1!=-2)>3?(4+5*6+7?8||9&&10:11):12^13r||14') +pgfluamath.ptransform_math_expr('-1^2\\test\\toto^3') +pgfluamath.ptransform_math_expr('{1,{2+3,4}[1],5}[2]+6') +pgfluamath.ptransform_math_expr('{1,{2+3,4},5}[1][1]') +pgfluamath.ptransform_math_expr('{1,{2,3},4}[1][1]') +pgfluamath.ptransform_math_expr('{1,{2,3}[1],4}[1]') + +--Loadstring: If it succeeds in converting the string to a *function*, it returns that function; otherwise, it returns nil and an error message. +toto = loadstring('texio.write_nl(' .. pgfluamath.transform_math_expr('{1,{2,3},4}[1][1]') .. ')') +toto() + +-- IMPORTANT NOTE!! +-- local function add (a,b) will fail within loadstring. Needs to be global. loadstring opens a new chunk that does not know the local variables of other chunks. + +--toto = loadstring('texio.write_nl(' .. pgfluamath.transform_math_expr('{1,{2,3E-1+7}[1],4}[1]') .. ')') +--toto() + +return pgfluamath |