1 Check whether a number is an integer
Maybe \int_eval
and \fp_eval
will support the feature you ask for in the future:
If people think it's useful I could probably adapt the l3fp
parser to provide a function to "parse until failure" and return the split token list, the result, and true/false. It could have an int
, dim
, skip
, fp
variants. — Bruno Le Floch on GitHub
Meanwhile you can check for a regular integer using a regular expression. In case you are wondering whether this regex could be extended to cover an expression that \int_eval
can digest, I have to disappoint you. Regular expressions do not support recursion and, as you can see, (1+(1+(1+1)))
for example is recursive.
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\prg_new_protected_conditional:Npnn \if_is_int:n #1 { T, F, TF }
{
\regex_match:nnTF { ^[\+\-]?[\d]+$ } {#1} % $
{ \prg_return_true: }
{ \prg_return_false: }
}
\if_is_int:nTF { 1 }
{ Is~integer }
{ Is~not~integer }
\par
\if_is_int:nTF { String }
{ Is~integer }
{ Is~not~integer }
\ExplSyntaxOff
\end{document}
No surprises in the output.
2 Check whether an expression can evaluate to an integer
If you are keen on knowing whether the expression will parse in \int_eval
, you could use LPEG (Lua Parsing Expression Grammar) to parse the mathematical expression. The advantage is that this is fully expandable. On the other hand it is completely pointless because then you could just evaluate the expression in Lua anyway.
The three snippets below are one continuous file but otherwise TeX.SX wouldn't let me mix syntax highlighting. (Thanks @Mico for the idea!)
\documentclass{article}
\pagestyle{empty}
\usepackage{expl3}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
local lpeg = require"lpeg"
local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
local white = S(" \t") ^ 0
local integer = white * R("09") ^ 1 * white
local exponent = white * P("^") * white
local muldiv = white * S("/*") * white
local addsub = white * S("+-") * white
local open = white * P("(") * white
local close = white * P(")") * white
local calculator = P({
"input",
input = V("expression") * -1,
expression = V("term") * (addsub * V("term"))^0,
term = V("primary") * (muldiv * V("primary"))^0,
primary = integer + ( open * V("expression") * close )
})
function check_integer(str)
if calculator:match(str) then
tex.print("\\prg_return_true:")
else
tex.print("\\prg_return_false:")
end
end
\end{luacode*}
\ExplSyntaxOn
\prg_new_conditional:Npnn \if_is_int:n #1 { p, T, F, TF }
{
\lua_now_x:n { check_integer([[\lua_escape_x:n {#1}]]) }
}
\if_is_int:nTF { 5*3+2 } { Is~integer } { Is~not~integer }\par
\if_is_int:nTF { 3*(5-7^8) } { Is~integer } { Is~not~integer }\par
\if_is_int:nTF { -(1) } { Is~integer } { Is~not~integer }\par
\if_is_int:nTF { String } { Is~integer } { Is~not~integer }\par
\ExplSyntaxOff
\end{document}
3 Implement your own integer calculator
Since we have long gone beyond what is sensible to do, why not go the extra mile and implement a complete mathematical expression parser and evaluator in Lua.
The thing is based off of my even more complete math parser for floating point numbers in Lua which can be found as a Gist on my GitHub (This one has even more bells and whistles and supports mathematical constants and functions).
\documentclass{article}
\usepackage{expl3}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
local lpeg = require"lpeg"
local C, P, R, S, V = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V
local white = S(" \t") ^ 0
local integer = white * C(R("09") ^ 1) * white / tonumber
local power = white * C(P("^")) * white
local muldiv = white * C(S("/*%")) * white
local addsub = white * C(S("+-")) * white
local open = white * P("(") * white
local close = white * P(")") * white
-- Evaluate AST recursively
local function eval(t)
if type(t) == "table" then
if t.op == "+" then return (t.left and eval(t.left) or 0) + eval(t.right)
elseif t.op == "-" then return (t.left and eval(t.left) or 0) - eval(t.right)
elseif t.op == "*" then return eval(t.left) * eval(t.right)
-- Below is my poor excuse for missing integer division before Lua 5.3
elseif t.op == "/" then return math.floor(eval(t.left) / eval(t.right))
elseif t.op == "%" then return eval(t.left) % eval(t.right)
elseif t.op == "^" then return eval(t.left) ^ eval(t.right)
else error("Cannot happen") end
elseif type(t) == "number" then
return t
else error("Cannot happen") end
end
-- Insert binary node into AST
local function binary(rule)
local function recurse(left,op,right,...)
if op then
return recurse({ op = op, left = left, right = right },...)
else
return left
end
end
return rule / recurse
end
-- Insert unary node into AST
local function unary(rule)
return rule / function(op,right)
return { op = op, right = right }
end
end
local grammar = P({
"input",
input = V("expression") * -1,
expression = binary( V("term") * ( addsub * V("term") )^0 ),
term = binary( V("factor") * ( muldiv * V("factor"))^0 ),
factor = binary( V("primary") * ( power * V("factor"))^0 ),
primary = integer
+ open * V("expression") * close
+ unary( addsub * V("primary") )
})
int = {
check = function(str)
if grammar:match(str) then
tex.print("\\prg_return_true:")
else
tex.print("\\prg_return_false:")
end
end,
eval = function(str)
local result = eval(assert(grammar:match(str)))
tex.print(string.format("%d",result))
end
}
\end{luacode*}
\ExplSyntaxOn
\prg_new_conditional:Npnn \if_lua_is_int:n #1 { p, T, F, TF }
{
\lua_now_x:n { int.check([[\lua_escape_x:n {#1}]]) }
}
\cs_new:Npn \lua_int_eval:n #1
{
\lua_now_x:n { int.eval([[\lua_escape_x:n {#1}]]) }
}
\if_lua_is_int:nTF { 5*3+2 } { \lua_int_eval:n { 5*3+2 } } { :( }\par
\if_lua_is_int:nTF { 3*(5-7^8) } { \lua_int_eval:n { 3*(5-7^8) } } { :( }\par
\if_lua_is_int:nTF { -(1) } { \lua_int_eval:n { -(1) } } { :( }\par
\if_lua_is_int:nTF { String } { \lua_int_eval:n { String } } { :( }\par
\ExplSyntaxOff
\end{document}
^
) or perhaps\macroA\macroB\macroC\macroD
where each\macroX
will expand to a part of the expression?\int_eval:n
accepts as an integer.