Skip to content

Instantly share code, notes, and snippets.

@erkanyildiz
Created January 5, 2015 09:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erkanyildiz/bfc514d3692e551e829e to your computer and use it in GitHub Desktop.
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.
// erkanyildiz
// 20150105-1132
//
// NSString+Evaluate.h
#import <Foundation/Foundation.h>
extern NSString* const kEYStringEvaluateError;
@interface NSString (Evaluate)
-(double)evaluate;
@end
// 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