Regex para emparejar funciones y capturar sus argumentos
Estoy trabajando en una calculadora y toma expresiones de cadena y las evalúa. Tengo una función que busca en la expresión funciones matemáticas usando Regex, recupera los argumentos, busca el nombre de la función y lo evalúa. Con lo que estoy teniendo problemas es que solo puedo hacer esto si sé cuántos argumentos habrá, no puedo entender bien el Regex. Y si acabo de dividir el contenido de la(
y)
personajes por el,
personaje entonces no puedo tener otras llamadas de función en ese argumento.
Aquí está el patrón de coincidencia de funciones:\b([a-z][a-z0-9_]*)\((..*)\)\b
Solo funciona con un argumento, ¿puedo crear un grupo para cada argumento excluyendo los que están dentro de las funciones anidadas? Por ejemplo, coincidiría con:func1(2 * 7, func2(3, 5))
y crea grupos de captura para:2 * 7
yfunc2(3, 5)
Aquí la función que estoy usando para evaluar la expresión:
/// <summary>
/// Attempts to evaluate and store the result of the given mathematical expression.
/// </summary>
public static bool Evaluate(string expr, ref double result)
{
expr = expr.ToLower();
try
{
// Matches for result identifiers, constants/variables objects, and functions.
MatchCollection results = Calculator.PatternResult.Matches(expr);
MatchCollection objs = Calculator.PatternObjId.Matches(expr);
MatchCollection funcs = Calculator.PatternFunc.Matches(expr);
// Parse the expression for functions.
foreach (Match match in funcs)
{
System.Windows.Forms.MessageBox.Show("Function found. - " + match.Groups[1].Value + "(" + match.Groups[2].Value + ")");
int argCount = 0;
List<string> args = new List<string>();
List<double> argVals = new List<double>();
string funcName = match.Groups[1].Value;
// Ensure the function exists.
if (_Functions.ContainsKey(funcName)) {
argCount = _Functions[funcName].ArgCount;
} else {
Error("The function '"+funcName+"' does not exist.");
return false;
}
// Create the pattern for matching arguments.
string argPattTmp = funcName + "\\(\\s*";
for (int i = 0; i < argCount; ++i)
argPattTmp += "(..*)" + ((i == argCount - 1) ? ",":"") + "\\s*";
argPattTmp += "\\)";
// Get all of the argument strings.
Regex argPatt = new Regex(argPattTmp);
// Evaluate and store all argument values.
foreach (Group argMatch in argPatt.Matches(match.Value.Trim())[0].Groups)
{
string arg = argMatch.Value.Trim();
System.Windows.Forms.MessageBox.Show(arg);
if (arg.Length > 0)
{
double argVal = 0;
// Check if the argument is a double or expression.
try {
argVal = Convert.ToDouble(arg);
} catch {
// Attempt to evaluate the arguments expression.
System.Windows.Forms.MessageBox.Show("Argument is an expression: " + arg);
if (!Evaluate(arg, ref argVal)) {
Error("Invalid arguments were passed to the function '" + funcName + "'.");
return false;
}
}
// Store the value of the argument.
System.Windows.Forms.MessageBox.Show("ArgVal = " + argVal.ToString());
argVals.Add(argVal);
}
else
{
Error("Invalid arguments were passed to the function '" + funcName + "'.");
return false;
}
}
// Parse the function and replace with the result.
double funcResult = RunFunction(funcName, argVals.ToArray());
expr = new Regex("\\b"+match.Value+"\\b").Replace(expr, funcResult.ToString());
}
// Final evaluation.
result = Program.Scripting.Eval(expr);
}
catch (Exception ex)
{
Error(ex.Message);
return false;
}
return true;
}
////////////////////////////////// ---- PATTERNS ---- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
/// <summary>
/// The pattern used for function calls.
/// </summary>
public static Regex PatternFunc = new Regex(@"([a-z][a-z0-9_]*)\((..*)\)");
Como puede ver, hay un intento bastante malo de construir un Regex para que coincida con los argumentos. No funciona
Todo lo que estoy tratando de hacer es extraer2 * 7
yfunc2(3, 5)
de la expresiónfunc1(2 * 7, func2(3, 5))
pero también debe funcionar para funciones con diferentes conteos de argumentos. Si hay una manera de hacer esto sin usar Regex, eso también es bueno.