0

I know how to add custom functions to NSNumber for NSExpression to work with it. But for use it i need to declarate a string like "FUNCTION(1, 'sin')". Is is any way to declarate it just like "sin(1)"?

Egor Kolyshkin
  • 117
  • 1
  • 10
  • You could use NSRegularExpression to reformat your string from `FUNCTION(*, 'sin')` to `sin(*)`. – Albert Renshaw Oct 26 '22 at 03:43
  • In practice, using RegEx for this is extremely difficult and often leads to `Catastrophic Backtracing`. Instead just a simple recursive approach to manually modifying the NSStrings works sufficiently. I've created an example here: https://stackoverflow.com/a/74202748/2057171 – Albert Renshaw Oct 27 '22 at 02:31

2 Answers2

0

No, you cannot extend the syntax understood by NSExpression(format:).

For advanced expression parsing and evaluating, use 3rd party solutions such as DDMathParser.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
0

The selected answer is, in my opinion, ridiculous. You can, of course, simply reformat your string to your desired custom function, no need to become dependent on an entire library.

In your case, something like the following would work just fine.

NSString *equation = @"2+sin(54.23+(2+sin(sin(3+5))))-4+(5-3)+cos(4)";//your equation here

NSArray *functionNames = @[@"sin", @"cos", @"tan"];//your supported functions here

for (NSString *functionName in functionNames) {
    
    NSString *functionPrefix = [NSString stringWithFormat:@"%@(", functionName];
    
    while ([equation containsString:functionPrefix]) {
        
        int parensLevel = 1;
        int functionParameterIndex = ((int)[equation rangeOfString:functionPrefix].location)+((int)functionPrefix.length);
        int characterIndex = functionParameterIndex;
        while (characterIndex < equation.length) {
            NSString *character = [equation substringWithRange:NSMakeRange(characterIndex, 1)];
            if ([character isEqualToString:@"("]) {
                parensLevel++;
            } else if ([character isEqualToString:@")"]) {
                parensLevel--;
            }
            if (parensLevel == 0) {
                break;
            }
            characterIndex++;
        }
        if (parensLevel != 0) {
            break;//parens weren't balanced, error handle as needed
        }
        
        NSString *functionParameter = [equation substringWithRange:NSMakeRange(functionParameterIndex, characterIndex-functionParameterIndex)];
        NSString *function = [NSString stringWithFormat:@"%@(%@)", functionName, functionParameter];
        
        equation = [equation stringByReplacingOccurrencesOfString:function withString:[NSString stringWithFormat:@"FUNCTION(%@,'%@')", functionParameter, functionName]];
        
    }
    
}

//po string = "2+FUNCTION(54.23+(2+FUNCTION(FUNCTION(3+5,'sin'),'sin')),'sin')-4+(5-3)+FUNCTION(4,'cos')"

I wrote this in Objective-C but it works converted to swift as well.

Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195