// // MPExpressionParser.m // MathPad // // Created by Kim Wittenburg on 16.11.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPExpressionParser.h" #import "MPExpression.h" #import "MPTokenStream.h" #import "MPTerm.h" #import "MPToken.h" #import "MPSumTerm.h" #import "MPProductTerm.h" #import "MPFactorialTerm.h" #import "MPElementaryFunctionTerm.h" #import "MPFunctionTerm.h" #import "MPPowerTerm.h" #import "MPNegatedTerm.h" #import "MPNumber.h" #import "MPVariable.h" #import "MPFactorialTerm.h" #define success() state = 0 #define fail() state = -1 @interface MPExpressionParser () @property (nonatomic, strong) NSArray *tokens; @property (nonatomic) NSUInteger currentTokenIndex; @property (nonatomic, strong) NSMutableArray *errors; @property (nonatomic, strong) NSMutableArray *summands; @property (nonatomic) BOOL summandNegative; @property (nonatomic, strong) NSMutableArray *factors; @property (nonatomic) BOOL factorNegative; @property (nonatomic, strong) NSMutableArray *functionStack; @property (nonatomic, strong) NSMutableArray *valueGroup; @property (nonatomic, strong) MPTerm *value; @property (nonatomic) NSUInteger errorTokenIndex; @end @implementation MPExpressionParser - (instancetype)initWithExpression:(MPExpression *)expression { self = [super init]; if (self) { _tokens = [expression allItemsInReferenceFrame:MPTokenReferenceFrame]; _expression = expression; } return self; } - (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors { return [self parseExpectingVariableDefinition:NO errors:errors]; } - (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag errors:(NSArray *__autoreleasing *)errors { MPParsedExpression *result = [[MPParsedExpression alloc] init]; self.currentTokenIndex = 0; BOOL hasVariableDefinition = NO; NSString *variableName = nil; [self skipWhitespaces]; if (self.currentToken.tokenType == MPVariableToken) { variableName = self.currentToken.stringValue; [self nextToken]; [self skipWhitespaces]; if (self.currentToken.tokenType == MPEqualsToken) { [self nextToken]; hasVariableDefinition = YES; } } if (flag && hasVariableDefinition) { result.definedVariable = variableName; } else if (flag) { [self addErrorWithCode:3 localizedDescription:NSLocalizedString(@"Expected Variable Definition.", nil)]; if (errors) { *errors = self.errors; } return nil; } else if (hasVariableDefinition) { [self addErrorWithCode:4 localizedDescription:NSLocalizedString(@"Unexpected Variable Definition.", nil)]; if (errors) { *errors = self.errors; } return nil; } else { self.currentTokenIndex = 0; } [self skipWhitespaces]; if (self.currentTokenIndex >= self.tokens.count) { [self addErrorWithCode:5 localizedDescription:NSLocalizedString(@"Empty Expression.", nil)]; if (errors) { *errors = self.errors; } return nil; } [self runParserMachine]; if ([self errorOccured]) { if (errors) { *errors = self.errors; } return nil; } result.term = [[MPSumTerm alloc] initWithSummands:self.summands]; return result; } - (void)runParserMachine { NSInteger state = 1; NSInteger backtraceState = 0; NSUInteger backtraceTokenIndex = self.currentTokenIndex; BOOL alternateRoute = NO; while (state != 0) { switch (state) { case -1: state = backtraceState; self.currentTokenIndex = backtraceTokenIndex; alternateRoute = YES; break; case 1: [self skipWhitespaces]; if (self.currentToken.tokenType == MPOperatorListToken) { self.factorNegative = [self parseOperatorList:self.currentToken]; [self nextToken]; state = 2; } else { self.factorNegative = NO; state = 2; } break; case 2: [self skipWhitespaces]; if (self.currentToken.tokenType == MPElementaryFunctionToken) { [self.functionStack addObject:self.currentToken.stringValue]; [self nextToken]; state = 2; } else { state = 3; } break; case 3: [self skipWhitespaces]; if (self.currentToken.tokenType == MPGenericFunctionToken) { NSArray *errors; self.value = [[MPFunctionTerm alloc] initWithFunction:(MPFunction *)self.currentToken errors:&errors]; if (!self.value) { [self.errors addObjectsFromArray:errors]; } [self nextToken]; [self runSuffixMachine]; if (self.value) { [self.valueGroup addObject:self.value]; } state = 6; } else { state = 4; } break; case 4: if (self.currentToken.tokenType == MPNumberToken) { self.value = [[MPNumber alloc] initWithNumber:[self parseNumber:self.currentToken]]; [self nextToken]; [self runSuffixMachine]; [self.valueGroup addObject:self.value]; state = 5; } else if (self.currentToken.tokenType == MPVariableToken) { self.value = [[MPVariable alloc] initWithVariableName:[self parseVariable:self.currentToken]]; [self nextToken]; [self runSuffixMachine]; [self.valueGroup addObject:self.value]; state = 5; } else { self.errorTokenIndex = self.currentTokenIndex; fail(); } break; case 5: if (alternateRoute) { alternateRoute = NO; state = 6; } else { backtraceState = 5; backtraceTokenIndex = self.currentTokenIndex; state = 4; } break; case 6: [self collapseFactor]; [self skipWhitespaces]; backtraceState = 6; backtraceTokenIndex = self.currentTokenIndex; if (!alternateRoute && self.currentToken.tokenType == MPOperatorListToken) { [self collapseSummand]; self.summandNegative = [self parseOperatorList:self.currentToken]; [self nextToken]; state = 1; } else { state = 7; } break; case 7: if (alternateRoute) { [self collapseSummand]; alternateRoute = NO; success(); } else { backtraceState = 7; backtraceTokenIndex = self.currentTokenIndex; state = 8; } break; case 8: [self skipWhitespaces]; if (self.currentToken.tokenType == MPMultiplicationSymbolToken) { [self nextToken]; state = 1; } else { state = 1; } break; default: // Should never get here break; } } } - (void)skipWhitespaces { while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) { [self nextToken]; } } - (id)currentToken { if (self.currentTokenIndex >= self.tokens.count) { return nil; } return self.tokens[self.currentTokenIndex]; } - (void)nextToken { self.currentTokenIndex++; } - (NSMutableArray *)errors { if (!_errors) { _errors = [[NSMutableArray alloc] init]; } return _errors; } - (NSMutableArray *)summands { if (!_summands) { _summands = [[NSMutableArray alloc] init]; } return _summands; } - (NSMutableArray *)factors { if (!_factors) { _factors = [[NSMutableArray alloc] init]; } return _factors; } - (NSMutableArray *)functionStack { if (!_functionStack) { _functionStack = [[NSMutableArray alloc] init]; } return _functionStack; } - (NSMutableArray *)valueGroup { if (!_valueGroup) { _valueGroup = [[NSMutableArray alloc] init]; } return _valueGroup; } - (BOOL)parseOperatorList:(id)token // Returns YES if list is overall negative { NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@"+" withString:@""]; return (operatorString.length & 1) == 1; } - (NSDecimalNumber *)parseNumber:(id)token { return [NSDecimalNumber decimalNumberWithString:token.stringValue locale:[NSLocale currentLocale]]; } - (NSString *)parseVariable:(id)token { return token.stringValue; } - (void)collapseSummand { if (self.factors.count > 0) { MPTerm *summand = [[MPProductTerm alloc] initWithFactors:self.factors]; if (self.summandNegative) { summand = [[MPNegatedTerm alloc] initWithTerm:summand]; } [self.summands addObject:summand]; } self.factors = nil; self.summandNegative = NO; } - (void)collapseFactor { if (self.valueGroup.count > 0) { MPTerm *factor = [[MPProductTerm alloc] initWithFactors:self.valueGroup]; for (NSInteger index = self.functionStack.count - 1; index >= 0; index--) { factor = [[MPElementaryFunctionTerm alloc] initWithFunctionIdentifier:self.functionStack[index] term:factor]; } if (self.factorNegative) { factor = [[MPNegatedTerm alloc] initWithTerm:factor]; } [self.factors addObject:factor]; } self.valueGroup = nil; self.functionStack = nil; self.factorNegative = NO; } - (void)runSuffixMachine { BOOL checkMore = YES; while (checkMore) { if (self.currentToken.tokenType == MPFactorialToken) { [self nextToken]; if (self.value) { MPFactorialTerm *term = [[MPFactorialTerm alloc] initWithTerm:self.value]; self.value = term; } } else if (self.currentToken.tokenType == MPPowerToken) { [self nextToken]; if (self.value) { NSArray *errors; MPPowerTerm *term = [[MPPowerTerm alloc] initWithFunction:(MPFunction *)self.currentToken errors:&errors]; if (!term) { [self.errors addObjectsFromArray:errors]; } term.baseTerm = self.value; self.value = term; } } else { checkMore = NO; } } } - (void)addErrorWithCode:(NSInteger)code localizedDescription:(NSString *)description { NSInteger errorIndex = [self.expression convertIndex:self.errorTokenIndex fromReferenceFrame:MPTokenReferenceFrame toReferenceFrame:MPSymbolReferenceFrame]; [self.errors addObject:[NSError errorWithDomain:MPMathKitErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: description, MPPathToExpressionKey: self.expression.indexPath, MPErrorIndexKey: @(errorIndex)}]]; } - (BOOL)errorOccured { [self skipWhitespaces]; if (self.currentTokenIndex < self.tokens.count) { if (self.errorTokenIndex >= self.tokens.count) { [self addErrorWithCode:0 localizedDescription:NSLocalizedString(@"Unexpected end. Expected Value.", nil)]; } else { [self addErrorWithCode:1 localizedDescription:NSLocalizedString(@"Unexpected Symbol. Expected Value", nil)]; } } return self.errors.count > 0; } @end