-- 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
