Created
January 5, 2015 09:37
-
-
Save erkanyildiz/bfc514d3692e551e829e to your computer and use it in GitHub Desktop.
A simple expression evaluator category on NSString which can handle adding, subtracting, multiplication and division operations with parentheses precedence.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// erkanyildiz | |
// 20150105-1132 | |
// | |
// NSString+Evaluate.h | |
#import <Foundation/Foundation.h> | |
extern NSString* const kEYStringEvaluateError; | |
@interface NSString (Evaluate) | |
-(double)evaluate; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// erkanyildiz | |
// 20150105-1132 | |
// | |
// NSString+Evaluate.m | |
#import "NSString+Evaluate.h" | |
@implementation NSString (Evaluate) | |
NSString* const kEYStringEvaluateError = @"kEYStringEvaluateError"; | |
char* c; | |
NSInteger parenthesisCount; | |
-(double)evaluate | |
{ | |
c = (char*)[self UTF8String]; | |
return [self start]; | |
} | |
-(double)error:(NSString*)description | |
{ | |
NSString *errorMessage = [@"Expression error: " stringByAppendingString:description]; | |
[NSNotificationCenter.defaultCenter postNotificationName:kEYStringEvaluateError object:errorMessage]; | |
return NAN; | |
} | |
#pragma mark - | |
-(double)start | |
{ | |
parenthesisCount = 0; | |
double result = [self summands]; | |
if(parenthesisCount != 0 || *c == ')') | |
return [self error:@"Unbalanced parentheses"]; | |
if (*c != '\0') | |
return [self error:@"Invalid character or missing operator"]; | |
return result; | |
} | |
-(double)summands | |
{ | |
double r1 = [self factors]; | |
while (1) | |
{ | |
[self skipWhiteSpace]; | |
char operator = *c; | |
if(operator != '+' && operator != '-') | |
return r1; | |
c++; | |
double r2 = [self factors]; | |
if(operator == '+') | |
r1 += r2; | |
else | |
r1 -= r2; | |
} | |
} | |
-(double)factors | |
{ | |
double r1 = [self atomics]; | |
while (1) | |
{ | |
[self skipWhiteSpace]; | |
char operator = *c; | |
if(operator != '*' && operator != '/') | |
return r1; | |
c++; | |
double r2 = [self atomics]; | |
if(operator == '*') | |
r1 *= r2; | |
else if (r2 != 0) | |
r1 /= r2; | |
else | |
return [self error:@"Division by zero"]; | |
} | |
} | |
-(double)atomics | |
{ | |
[self skipWhiteSpace]; | |
if(*c == '(') | |
{ | |
parenthesisCount++; | |
c++; | |
double result = [self summands]; | |
if (*c != ')') | |
return [self error:@"Unbalanced parentheses within atomics"]; | |
c++; | |
parenthesisCount--; | |
return result; | |
} | |
char* end; | |
double result = strtod(c, &end); | |
if(end == c) | |
return [self error:@"Invalid character or missing operator within atomics"]; | |
c = end; | |
return result; | |
} | |
-(void)skipWhiteSpace | |
{ | |
while (*c == ' ' || *c == '\n' || *c == '\t') | |
c++; | |
} | |
/* | |
#pragma mark - | |
+(void)testExpressions | |
{ | |
NSArray* testExpressions = @[ | |
@{@"exp":@"3+4", @"res":@7}, | |
@{@"exp":@"100-20", @"res":@80}, | |
@{@"exp":@"5*6", @"res":@30}, | |
@{@"exp":@"100/3", @"res":@33.33333333}, | |
@{@"exp":@"1.4*3.67", @"res":@(5.138)}, | |
@{@"exp":@".2*5.0", @"res":@(1)}, | |
@{@"exp":@"-.2*5.0", @"res":@(-1)}, | |
@{@"exp":@"-4*-3.0", @"res":@(12)}, | |
@{@"exp":@"40*4-10/2+6", @"res":@(161)}, | |
@{@"exp":@"(-5)", @"res":@(-5)}, | |
@{@"exp":@"48/4-(5*(9-3))", @"res":@(-18)}, | |
@{@"exp":@"4*(5+7*(10/2))*9", @"res":@1440}, | |
@{@"exp":@"4+3.5*8", @"res":@32}, | |
@{@"exp":@"5-6*8+9-7", @"res":@(-41)}, | |
@{@"exp":@"1-5*(-6)-6", @"res":@25}, | |
@{@"exp":@"1/2/2/2", @"res":@(0.125)}, | |
@{@"exp":@"3 + 5", @"res":@(8)}, | |
@{@"exp":@"-4*(2+1)", @"res":@(-12)}, | |
@{@"exp":@"4*(5+7*10/2))*9", @"res":@(NAN)}, | |
@{@"exp":@"8(9*(5+1))", @"res":@(NAN)}, | |
@{@"exp":@"7/0", @"res":@(NAN)}, | |
@{@"exp":@"50/(5-5)", @"res":@(NAN)}, | |
@{@"exp":@"7//2", @"res":@(NAN)}, | |
@{@"exp":@"zxcv1*5", @"res":@(NAN)}, | |
]; | |
[testExpressions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) | |
{ | |
NSLog(@"Test %lu\n" | |
"Expression: %@\n" | |
"Expected Result: %g\n" | |
"Calculated Result: %f\n\n", | |
idx+1, | |
obj[@"exp"], | |
[obj[@"res"] doubleValue], | |
[obj[@"exp"] evaluate] ); | |
}]; | |
} | |
*/ | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment