// // 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" #import "MPFunction.h" #import "MPMathRules.h" #import "MPToken.h" #import "MPFunction+MPToken.h" #import "MPTokenStream.h" #import "MPEvaluationContext.h" #import "MPPowerFunction.h" @interface MPExpressionEvaluator () @property (readwrite, nonatomic, copy) NSString *definedVariable; @property (nonatomic, strong) NSArray *tokens; - (void)setError:(MPParseError *)error; @end @implementation MPExpressionEvaluator { MPParseError *__autoreleasing *_error; MPTokenStream *tokenStream; } - (id)initWithExpression:(MPExpression *)expression { self = [super init]; if (self) { _expression = expression; } return self; } @synthesize lexer = _lexer; - (void)setLexer:(MPExpressionTokenizer *)lexer { _lexer = lexer; self.tokens = nil; } - (MPExpressionTokenizer *)lexer { 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; } // Variable Definition if (flag) { MPToken *variableToken = [tokenStream nextToken]; MPToken *equalsToken = [tokenStream nextTokenOfType:MPEqualsToken]; 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 [self decoratedTerm:[[MPTerm alloc] initWithNumber:token.number]]; } case MPVariableToken: { if(![[MPEvaluationContext sharedContext] isVariableDefined:token.variable]) { self.error = MPParseError(token.range, @"Undefined Variable"); return nil; } return [self decoratedTerm:[[MPTerm alloc] initWithVariable:token.variable]]; } case MPGenericFunctionToken: { return [self decoratedTerm:[((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; } } } - (MPTerm *)decoratedTerm:(MPTerm *)term { MPTerm *decoratedTerm = term; MPToken *facultyToken = [tokenStream nextTokenOfType:MPFactorialToken]; if (facultyToken) { decoratedTerm = [[MPTerm alloc] initWithFactorialOfTerm:term]; } MPToken *powerToken = [tokenStream nextTokenOfType:MPGenericFunctionToken]; if ([powerToken isKindOfClass:[MPPowerFunction class]]) { MPPowerFunction *powerFunction = (MPPowerFunction *)powerToken; powerFunction.baseTerm = decoratedTerm; return [powerFunction parseWithError:_error]; } else { tokenStream.currentLocation--; } return decoratedTerm; } @end