Improved Evaluation
This commit is contained in:
@@ -9,105 +9,236 @@
|
||||
#import "MPExpressionEvaluator.h"
|
||||
#import "MPExpression.h"
|
||||
#import "MPFunction.h"
|
||||
#import "MPParsedFactor.h"
|
||||
#import "MPFunction+MPParsedFactor.h"
|
||||
#import "MPMathRules.h"
|
||||
#import "MPToken.h"
|
||||
#import "MPFunction+MPToken.h"
|
||||
#import "MPTokenStream.h"
|
||||
|
||||
#import "MPEvaluationContext.h"
|
||||
|
||||
|
||||
|
||||
@interface MPExpressionEvaluator ()
|
||||
@property (readwrite, nonatomic, strong) NSString *definedVariable;
|
||||
@property (readwrite, nonatomic, copy) NSString *definedVariable;
|
||||
@property (nonatomic, strong) NSArray *tokens;
|
||||
|
||||
- (void)setError:(MPParseError *)error;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpressionEvaluator {
|
||||
MPElementParser *parser;
|
||||
MPParseError *__autoreleasing *_error;
|
||||
MPTokenStream *tokenStream;
|
||||
}
|
||||
|
||||
- (id)initWithExpression:(MPExpression *)expression
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_expression = expression;
|
||||
parser = [[MPElementParser alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Evaluating Expressions
|
||||
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
||||
@synthesize lexer = _lexer;
|
||||
|
||||
- (void)setLexer:(MPExpressionTokenizer *)lexer
|
||||
{
|
||||
return [self evaluateVariableDefinition:NO error:error];
|
||||
_lexer = lexer;
|
||||
self.tokens = nil;
|
||||
}
|
||||
|
||||
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error
|
||||
- (MPExpressionTokenizer *)lexer
|
||||
{
|
||||
// Empty Expression
|
||||
if (self.expression.numberOfElements == 0) {
|
||||
*error = MPParseError(NSMakeRange(0, 0), @"Expected Expression");
|
||||
if (!_lexer) {
|
||||
_lexer = [[MPExpressionTokenizer alloc] init];
|
||||
}
|
||||
return _lexer;
|
||||
}
|
||||
|
||||
- (NSArray *)tokens
|
||||
{
|
||||
if (!_tokens) {
|
||||
_tokens = [self.lexer tokenizeExpression:self.expression];
|
||||
}
|
||||
return _tokens;
|
||||
}
|
||||
|
||||
- (void)expressionDidChangeInRange:(NSRange)range
|
||||
replacementLength:(NSUInteger)replacementLength
|
||||
{
|
||||
self.tokens = nil;
|
||||
}
|
||||
|
||||
#pragma mark Evaluating Expressions
|
||||
- (void)setError:(MPParseError *)error
|
||||
{
|
||||
if (_error) {
|
||||
error.pathToExpression = self.expression.indexPath;
|
||||
*_error = error;
|
||||
}
|
||||
}
|
||||
|
||||
- (MPTerm *)parseExpectingVariable:(BOOL)flag
|
||||
error:(MPParseError *__autoreleasing *)error
|
||||
{
|
||||
_error = error;
|
||||
tokenStream = [[MPTokenStream alloc] initWithTokens:self.tokens];
|
||||
if (!tokenStream.hasMoreTokens) {
|
||||
self.error = MPParseError(NSMakeRange(0, 0), @"Empty Expression");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *products = [[NSMutableArray alloc] init];
|
||||
MPParsedProduct *currentProduct = nil;
|
||||
|
||||
for (NSUInteger elementIndex = 0; elementIndex < self.expression.numberOfElements; elementIndex++) {
|
||||
// Variable Definition
|
||||
if (flag) {
|
||||
MPToken *variableToken = [tokenStream nextToken];
|
||||
MPToken *equalsToken = [tokenStream nextTokenOfType:MPEqualsToken];
|
||||
|
||||
id<MPExpressionElement> element = [self.expression elementAtIndex:elementIndex];
|
||||
|
||||
if ([element isString]) {
|
||||
id<MPParsedFactor> nextFactor = nil;
|
||||
if (elementIndex < self.expression.numberOfElements - 1) {
|
||||
MPFunction *nextFunction = (MPFunction *)[self.expression elementAtIndex:elementIndex+1];
|
||||
nextFactor = nextFunction;
|
||||
}
|
||||
|
||||
NSArray *newProducts;
|
||||
if (elementIndex == 0 && flag) {
|
||||
NSString *definedVariable;
|
||||
newProducts = [parser parseElement:(NSString *)element
|
||||
previousProduct:currentProduct
|
||||
nextFactor:nextFactor
|
||||
definesVariable:YES
|
||||
definedVariable:&definedVariable
|
||||
error:error];
|
||||
self.definedVariable = definedVariable;
|
||||
} else {
|
||||
newProducts = [parser parseElement:(NSString *)element
|
||||
previousProduct:currentProduct
|
||||
nextFactor:nextFactor
|
||||
error:error];
|
||||
}
|
||||
if (!newProducts) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (NSUInteger productIndex = 0; productIndex < newProducts.count-1; productIndex++) {
|
||||
[products addObject:newProducts[productIndex]];
|
||||
}
|
||||
currentProduct = newProducts.lastObject;
|
||||
|
||||
elementIndex++;
|
||||
} else {
|
||||
if (!currentProduct) {
|
||||
currentProduct = [[MPParsedProduct alloc] init];
|
||||
} else if ([MPMathRules sharedRules].allowsImplicitMultiplication) {
|
||||
[currentProduct addFactor:(MPFunction *)element];
|
||||
} else {
|
||||
*error = MPParseError(NSMakeRange(((MPFunction *)element).range.location, 0), @"Implicit Multiplication not allowed");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[products addObject:currentProduct];
|
||||
|
||||
NSDecimalNumber *value = [NSDecimalNumber zero];
|
||||
for (MPParsedProduct *product in products) {
|
||||
NSDecimalNumber *productValue = [product evaluateWithError:error];
|
||||
if (!productValue) {
|
||||
if (variableToken.tokenType != MPVariableToken) {
|
||||
self.error = MPParseError(variableToken.range, @"Expected Variable Definition.");
|
||||
return nil;
|
||||
}
|
||||
if (equalsToken.tokenType != MPEqualsToken) {
|
||||
self.error = MPParseError(equalsToken.range, @"Expected \"=\".");
|
||||
return nil;
|
||||
}
|
||||
self.definedVariable = variableToken.stringValue;
|
||||
}
|
||||
|
||||
BOOL isAtBeginning = YES;
|
||||
NSMutableArray *summands = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *currentSummand = [[NSMutableArray alloc] init];
|
||||
|
||||
while (tokenStream.hasMoreTokens) {
|
||||
MPToken *multiplicationToken = [tokenStream nextTokenOfType:MPMultiplicationSymbolToken];
|
||||
MPToken *operatorToken = [tokenStream nextTokenOfType:MPOperatorListToken];
|
||||
NSRange valueRange;
|
||||
MPTerm *value = [self nextValue:&valueRange];
|
||||
|
||||
if (multiplicationToken) {
|
||||
if (isAtBeginning) {
|
||||
self.error = MPParseError(multiplicationToken.range, @"Unexpected Symbol. Expected Number.");
|
||||
return nil;
|
||||
}
|
||||
if (operatorToken) {
|
||||
if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) {
|
||||
self.error = MPParseError(operatorToken.range, @"Too many operators in multiplication");
|
||||
return nil;
|
||||
}
|
||||
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
|
||||
}
|
||||
if (value) {
|
||||
[currentSummand addObject:value];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
if (operatorToken) {
|
||||
if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLength]) {
|
||||
self.error = MPParseError(operatorToken.range, @"Too many operators.");
|
||||
return nil;
|
||||
}
|
||||
[summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
|
||||
currentSummand = [[NSMutableArray alloc] init];
|
||||
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
|
||||
if (value) {
|
||||
[currentSummand addObject:value];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} else if (!value) {
|
||||
return nil;
|
||||
} else if (isAtBeginning || [[MPMathRules sharedRules] allowsImplicitMultiplication]) {
|
||||
[currentSummand addObject:value];
|
||||
} else {
|
||||
self.error = MPParseError(NSMakeRange(valueRange.location, 0), @"Implicit Multiplication not allowed here");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
isAtBeginning = NO;
|
||||
}
|
||||
[summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
|
||||
return [[MPTerm alloc] initWithSummands:summands.copy];
|
||||
}
|
||||
|
||||
- (MPTerm *)nextValue:(NSRangePointer)range
|
||||
{
|
||||
MPToken *token = [tokenStream nextToken];
|
||||
if (range) {
|
||||
*range = token.range;
|
||||
}
|
||||
switch (token.tokenType) {
|
||||
case MPNumberToken:
|
||||
{
|
||||
return [[MPTerm alloc] initWithNumber:token.number];
|
||||
}
|
||||
|
||||
case MPVariableToken:
|
||||
{
|
||||
if(![[MPEvaluationContext sharedContext] isVariableDefined:token.variable]) {
|
||||
self.error = MPParseError(token.range, @"Undefined Variable");
|
||||
return nil;
|
||||
}
|
||||
return [[MPTerm alloc] initWithVariable:token.variable];
|
||||
}
|
||||
|
||||
case MPGenericFunctionToken:
|
||||
{
|
||||
return [((MPFunction *)token) parseWithError:_error];
|
||||
}
|
||||
|
||||
case MPSinToken:
|
||||
{
|
||||
NSRange sinTermRange;
|
||||
MPTerm *sinTerm = [self nextValue:&sinTermRange];
|
||||
if (!sinTerm) {
|
||||
if (range) {
|
||||
*range = NSUnionRange(token.range, sinTermRange);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
return [[MPTerm alloc] initWithSinOfTerm:sinTerm];
|
||||
}
|
||||
|
||||
case MPCosToken:
|
||||
{
|
||||
NSRange cosTermRange;
|
||||
MPTerm *cosTerm = [self nextValue:&cosTermRange];
|
||||
if (!cosTerm) {
|
||||
if (range) {
|
||||
*range = NSUnionRange(token.range, cosTermRange);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
return [[MPTerm alloc] initWithSinOfTerm:cosTerm];
|
||||
}
|
||||
|
||||
case MPTanToken:
|
||||
{
|
||||
NSRange tanTermRange;
|
||||
MPTerm *tanTerm = [self nextValue:&tanTermRange];
|
||||
if (!tanTerm) {
|
||||
if (range) {
|
||||
*range = NSUnionRange(token.range, tanTermRange);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
return [[MPTerm alloc] initWithTanOfTerm:tanTerm];
|
||||
}
|
||||
|
||||
case MPEOFToken:
|
||||
{
|
||||
self.error = MPParseError(token.range, @"Unexpected End. Expected Number.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
self.error = MPParseError(token.range, @"Unexpected Symbol. Expected Number.");
|
||||
return nil;
|
||||
}
|
||||
value = [value decimalNumberByAdding:productValue];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user