// // MPElementParser.m // MathPad // // Created by Kim Wittenburg on 10.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPElementParser.h" #import "NSRegularExpression+MPParsingAdditions.h" #import "MPParsedFactor.h" #import "MPParsedNumber.h" #import "MPParsedVariable.h" #import "MPParsedOperator.h" #define MPMultiplicationSymbol @"*" @interface MPElementParser () @property (nonatomic) NSString *input; @property (nonatomic) NSUInteger parsePosition; - (void)setError:(MPParseError *)error; - (BOOL)isAtEnd; - (NSRange)parseVariableDefinition:(NSString *__autoreleasing *)variableName; - (NSRange)parseWhitespaces; - (NSRange)parseMultiplicationSymbol; - (MPParsedOperator *)parseOperators; - (MPParsedNumber *)parseNumber; @end @implementation MPElementParser { MPParseError *__autoreleasing *outError; } static BOOL useUserDefaults; #pragma mark Creation Methods + (void)initialize { useUserDefaults = YES; } - (instancetype)init { self = [super init]; if (self) { self.allowsImplicitMultiplications = NO; self.maximumOperatorChainLength = 2; self.maximumOperatorChainLengthInMultiplication = 1; } return self; } #pragma mark Properties + (BOOL)isUsingDefaultValuesFromUserDefaults { return useUserDefaults; } + (void)setUsingDefaultValuesFromUserDefaults:(BOOL)flag { useUserDefaults = flag; } #pragma mark Parsing Methods - (BOOL)isAtEnd { return self.parsePosition >= self.input.length; } - (void)setError:(MPParseError *)error { if (outError != NULL) { *outError = error; } } - (NSArray *)parseElement:(NSString *)string previousProduct:(MPParsedProduct *)previousProduct nextFactor:(id)nextFactor error:(out MPParseError *__autoreleasing *)error { return [self parseElement:string previousProduct:previousProduct nextFactor:nextFactor definesVariable:NO definedVariable:NULL error:error]; } - (NSArray *)parseElement:(NSString *)string previousProduct:(MPParsedProduct *)previousProduct nextFactor:(id)nextFactor definesVariable:(BOOL)flag definedVariable:(NSString *__autoreleasing *)variableName error:(out MPParseError *__autoreleasing *)error { self.input = string; outError = error; self.parsePosition = 0; NSString *definedVariable; NSRange variableDefinitionRange = [self parseVariableDefinition:&definedVariable]; if (flag) { if (!definedVariable) { self.error = MPParseError(NSMakeRange(0, 0), @"Expected Variable Definition"); return nil; } else if (variableName) { *variableName = definedVariable; } } else if (definedVariable) { self.error = MPParseError(variableDefinitionRange, @"Unexpected Variable Definition"); return nil; } NSMutableArray *products = [[NSMutableArray alloc] init]; MPParsedProduct *currentProduct = previousProduct; while (YES) { [self parseWhitespaces]; NSRange multiplicationSymbolRange = [self parseMultiplicationSymbol]; BOOL hasMultiplicationSymbol = multiplicationSymbolRange.location != NSNotFound; MPParsedOperator *operators = [self parseOperators]; // NSRange functionRange = ... // BOOL hasFunction = functionRange.location != NSNotFound; MPParsedNumber *number = [self parseNumber]; if (!number.exists) { if ([self isAtEnd] && nextFactor != nil) { if (hasMultiplicationSymbol) { if (operators.numberOfOperators > self.maximumOperatorChainLengthInFunction) { self.error = MPParseError(operators.range, @"Too many operators in multiplication."); return nil; } [currentProduct addFactor:operators]; } else if (operators.exists) { if (operators.numberOfOperators > self.maximumOperatorChainLength) { self.error = MPParseError(operators.range, @"Too many operators."); return nil; } [products addObject:currentProduct]; currentProduct = [[MPParsedProduct alloc] init]; [currentProduct addFactor:operators]; } else if (self.allowsImplicitMultiplications) { NSLog(@"Here I am"); if (!currentProduct) { currentProduct = [[MPParsedProduct alloc] init]; } } else { NSLog(@"ERR"); self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Implicit Multiplication not allowed."); return nil; } break; } else if ([self isAtEnd]) { if (hasMultiplicationSymbol || operators.exists) { self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Unexpected End. Expected Number."); return nil; } } else { self.error = MPParseError(NSMakeRange(self.parsePosition, 1), @"Unexpected Symbol. Expected Number."); return nil; } break; } else { if (hasMultiplicationSymbol) { if (currentProduct) { if (operators.numberOfOperators > self.maximumOperatorChainLengthInMultiplication) { self.error = MPParseError(operators.range, @"Too many operators in multiplication."); return nil; } [currentProduct addFactor:operators]; [currentProduct addFactor:number]; } else { self.error = MPParseError(multiplicationSymbolRange, @"Unexpected Symbol. Expected Number."); return nil; } } else if (operators.exists) { if (operators.numberOfOperators > self.maximumOperatorChainLength) { self.error = MPParseError(operators.range, @"Too many operators."); return nil; } if (currentProduct) { [products addObject:currentProduct]; } currentProduct = [[MPParsedProduct alloc] initWithFactor:operators]; [currentProduct addFactor:number]; } else if (!currentProduct) { currentProduct = [[MPParsedProduct alloc] initWithFactor:number]; } else if (self.allowsImplicitMultiplications) { [currentProduct addFactor:number]; } else { self.error = MPParseError(NSMakeRange(number.range.location, 0), @"Implicit Multiplication not allowed."); return nil; } } } if (nextFactor) { [currentProduct addFactor:nextFactor]; } [products addObject:currentProduct]; return products; } static NSRegularExpression *variableDefinitionRegex; - (NSRange)parseVariableDefinition:(NSString *__autoreleasing *)variableName { if (!variableDefinitionRegex) { variableDefinitionRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([A-Za-z])\\s*=\\s*" options:0 error:NULL]; } NSTextCheckingResult *match = [variableDefinitionRegex firstMatchInString:self.input fromIndex:self.parsePosition]; if (!match) { return NSMakeRange(NSNotFound, 0); } self.parsePosition = NSMaxRange(match.range); NSRange variableDefinitionRange = [match rangeAtIndex:1]; *variableName = [self.input substringWithRange:variableDefinitionRange]; return variableDefinitionRange; } static NSRegularExpression *whitespaceRegex; - (NSRange)parseWhitespaces { if (!whitespaceRegex) { whitespaceRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*" options:0 error:NULL]; } NSTextCheckingResult *match = [whitespaceRegex firstMatchInString:self.input fromIndex:self.parsePosition]; if (match) { self.parsePosition = NSMaxRange(match.range); return match.range; } else { return NSMakeRange(NSNotFound, 0); } } static NSRegularExpression *multiplicationSymbolRegex; - (NSRange)parseMultiplicationSymbol { if (!multiplicationSymbolRegex) { NSString *multiplicationSymbolString = [NSRegularExpression escapedPatternForString:MPMultiplicationSymbol]; NSString *multiplicationSymbolRegexString = [NSString stringWithFormat:@"\\A\\s*(%@)\\s*", multiplicationSymbolString]; multiplicationSymbolRegex = [NSRegularExpression regularExpressionWithPattern:multiplicationSymbolRegexString options:0 error:NULL]; } NSTextCheckingResult *match = [multiplicationSymbolRegex firstMatchInString:self.input fromIndex:self.parsePosition]; if (!match) { return NSMakeRange(NSNotFound, 0); } self.parsePosition = NSMaxRange(match.range); return [match rangeAtIndex:1]; } static NSRegularExpression *operatorsRegex; - (MPParsedOperator *)parseOperators { if (!operatorsRegex) { operatorsRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([+-](?:\\s*[+-])*)\\s*" options:0 error:NULL]; } NSTextCheckingResult *match = [operatorsRegex firstMatchInString:self.input fromIndex:self.parsePosition]; NSRange matchRange; if (!match) { matchRange = NSMakeRange(NSNotFound, 0); } else { self.parsePosition = NSMaxRange(match.range); matchRange = [match rangeAtIndex:1]; } return [[MPParsedOperator alloc] initWithRange:matchRange inString:self.input]; } - (MPParsedNumber *)parseNumber { NSString *decimalSeparatorRegexString = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]]; NSString *numberRegexFormat = [NSString stringWithFormat:@"\\A\\s*((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))\\s*", decimalSeparatorRegexString, decimalSeparatorRegexString]; NSRegularExpression *numberRegex = [NSRegularExpression regularExpressionWithPattern:numberRegexFormat options:0 error:NULL]; NSTextCheckingResult *match = [numberRegex firstMatchInString:self.input fromIndex:self.parsePosition]; NSRange matchRange; if (!match) { matchRange = NSMakeRange(NSNotFound, 0); } else { self.parsePosition = NSMaxRange(match.range); matchRange = [match rangeAtIndex:1]; } return [[MPParsedNumber alloc] initWithRange:matchRange inString:self.input]; } @end