199 lines
7.0 KiB
Objective-C
199 lines
7.0 KiB
Objective-C
//
|
|
// MPExpressionEvaluator.m
|
|
// MathPad
|
|
//
|
|
// Created by Kim Wittenburg on 31.08.14.
|
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
|
//
|
|
|
|
#import "MPExpressionEvaluator.h"
|
|
#import "MPExpression.h"
|
|
|
|
@interface MPExpressionEvaluator ()
|
|
@property (readwrite, nonatomic, strong) NSString *definedVariable;
|
|
@end
|
|
|
|
@implementation MPExpressionEvaluator {
|
|
NSMutableDictionary *_variableBindings;
|
|
MPElementParser *parser;
|
|
}
|
|
- (id)initWithExpression:(MPExpression *)expression
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_expression = expression;
|
|
_variableBindings = [[NSMutableDictionary alloc] init];
|
|
parser = [[MPElementParser alloc] init];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark Evaluating Expressions
|
|
- (NSDictionary *)variableBindings
|
|
{
|
|
return [_variableBindings copy];
|
|
}
|
|
|
|
- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name
|
|
{
|
|
[_variableBindings setObject:value
|
|
forKey:name];
|
|
}
|
|
|
|
- (void)unbindVariableName:(NSString *)name
|
|
{
|
|
[_variableBindings removeObjectForKey:name];
|
|
}
|
|
|
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
|
{
|
|
return [self evaluateVariableDefinition:NO error:error];
|
|
}
|
|
|
|
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error
|
|
{
|
|
#warning Implement Cache
|
|
// Empty Expression
|
|
if (self.expression.numberOfElements == 0) {
|
|
if (error) {
|
|
*error = MPParseError(0, @"Empty Expression");
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
// Expression of just one element
|
|
if (self.expression.numberOfElements == 1) {
|
|
id<MPExpressionElement> singleElement = [self.expression elementAtIndex:0];
|
|
if ([singleElement isString]) {
|
|
MPParsedElement *parseResult = [parser parseElement:(NSString *)singleElement
|
|
error:error];
|
|
if ([parseResult isValidStandaloneElement:error]) {
|
|
if (flag && parseResult.definedVariable) {
|
|
self.definedVariable = parseResult.definedVariable;
|
|
} else if (flag) {
|
|
if (error) {
|
|
*error = MPParseError(0, @"Expected Variable Definition");
|
|
}
|
|
return nil;
|
|
} else if (parseResult.definedVariable) {
|
|
if (error) {
|
|
*error = MPParseError(0, @"Unexpected Variable Definition");
|
|
}
|
|
return nil;
|
|
}
|
|
return parseResult.standaloneValue;
|
|
} else {
|
|
return nil;
|
|
}
|
|
} else {
|
|
return [singleElement evaluate:error];
|
|
}
|
|
}
|
|
|
|
// Expression with any number of elements
|
|
NSDecimalNumber *value = [NSDecimalNumber zero];
|
|
NSDecimalNumber *currentSummand = nil;
|
|
|
|
// Process the first element
|
|
id<MPExpressionElement> firstElement = [self.expression elementAtIndex:0];
|
|
if ([firstElement isString]) {
|
|
MPParsedElement *parseResult = [parser parseElement:(NSString *)firstElement
|
|
error:error];
|
|
if (parseResult && [parseResult isValidElementAtBeginning:error]) {
|
|
if (flag && parseResult.definedVariable) {
|
|
self.definedVariable = parseResult.definedVariable;
|
|
} else if (flag) {
|
|
if (error) {
|
|
*error = MPParseError(0, @"Expected Variable Definition");
|
|
}
|
|
return nil;
|
|
} else if (parseResult.definedVariable) {
|
|
if (error) {
|
|
*error = MPParseError(0, @"Unexpected Variable Definition");
|
|
}
|
|
return nil;
|
|
}
|
|
if ([parseResult isFactor]) {
|
|
currentSummand = parseResult.value;
|
|
} else {
|
|
value = [parseResult valueAtBeginning];
|
|
currentSummand = parseResult.suffixMultiplicator;
|
|
}
|
|
}
|
|
} else {
|
|
currentSummand = [firstElement evaluate:error];
|
|
}
|
|
if (!currentSummand) {
|
|
return nil;
|
|
}
|
|
|
|
// Process the elements between the first and last element
|
|
for (NSUInteger index = 1; index < self.expression.numberOfElements-1; index++) {
|
|
id<MPExpressionElement> element = [self.expression elementAtIndex:index];
|
|
if ([element isString]) {
|
|
MPParsedElement *parseResult = [parser parseElement:(NSString *)element
|
|
error:error];
|
|
if (parseResult) {
|
|
if (parseResult.definedVariable) {
|
|
if (error) {
|
|
*error = MPParseError(index, @"Unexpected Variable Definition");
|
|
}
|
|
return nil;
|
|
}
|
|
if ([parseResult isFactor]) {
|
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
|
|
} else {
|
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
|
|
value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
|
|
currentSummand = parseResult.suffixMultiplicator;
|
|
}
|
|
} else {
|
|
return nil;
|
|
}
|
|
} else {
|
|
NSDecimalNumber *result = [element evaluate:error];
|
|
if (result) {
|
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process the last element
|
|
id<MPExpressionElement> lastElement = [self.expression elementAtIndex:self.expression.numberOfElements-1];
|
|
if ([lastElement isString]) {
|
|
MPParsedElement *parseResult = [parser parseElement:(NSString *)lastElement
|
|
error:error];
|
|
if (parseResult && [parseResult isValidElementAtEnd:error]) {
|
|
if (parseResult.definedVariable) {
|
|
if (error) {
|
|
*error = MPParseError(0, @"Unexpected Variable Definition");
|
|
}
|
|
return nil;
|
|
}
|
|
if ([parseResult isFactor]) {
|
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
|
|
} else {
|
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
|
|
value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
|
|
currentSummand = parseResult.suffixMultiplicator;
|
|
}
|
|
} else {
|
|
return nil;
|
|
}
|
|
} else {
|
|
NSDecimalNumber *result = [lastElement evaluate:error];
|
|
if (result) {
|
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
value = [value decimalNumberByAdding:currentSummand];
|
|
|
|
return value;
|
|
}
|
|
|
|
@end
|