// // MPElementParser.m // MathPad // // Created by Kim Wittenburg on 06.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPElementParser.h" @implementation MPElementParser { NSScanner *scanner; } - (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error { // Setup the Scanner scanner = [[NSScanner alloc] initWithString:string]; scanner.charactersToBeSkipped = [NSCharacterSet whitespaceAndNewlineCharacterSet]; scanner.caseSensitive = YES; MPParsedElement *parsedElement = [[MPParsedElement alloc] init]; // Scan the defined variable NSString *firstCharacters = nil; BOOL hasLettersInFront = [scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:&firstCharacters]; BOOL foundEquals = [scanner scanString:@"=" intoString:NULL]; if (hasLettersInFront && firstCharacters.length == 1 && foundEquals) { // Found variable definition parsedElement.definedVariable = firstCharacters; parsedElement.afterVariableDefinitionIndex = scanner.scanLocation; } else { // No variable definition found, reset the scanner parsedElement.definedVariable = nil; scanner.scanLocation = 0; } // Scan the Prefix BOOL startsWithAsterisk = [scanner scanString:@"*" intoString:NULL]; BOOL productClosed; NSDecimalNumber *prefix = [self scanClosedProduct:&productClosed error:NULL]; // Simple Factor if (scanner.isAtEnd) { parsedElement.isFactor = YES; parsedElement.value = prefix == nil ? [NSDecimalNumber one] : prefix; parsedElement.prefixOperatorExplicit = startsWithAsterisk; parsedElement.suffixOperatorExplicit = !productClosed; parsedElement.suffixIndex = scanner.scanLocation; return parsedElement; } else if (!productClosed) { if (error) { *error = MPParseError(scanner.scanLocation, @"Missing Number"); } return nil; } parsedElement.prefixOperatorExplicit = startsWithAsterisk; parsedElement.prefixValueExplicit = YES; parsedElement.prefixMultiplicator = prefix; parsedElement.suffixValueExplicit = YES; // Scan Summands NSDecimalNumber *value = [NSDecimalNumber zero]; NSDecimalNumber *currentSummand; while (!scanner.isAtEnd) { // Add every summand but the last one if (currentSummand != nil) { value = [value decimalNumberByAdding:currentSummand]; } // Find the operator (+ or -) NSString *operator; BOOL found = [scanner scanString:@"+" intoString:&operator]; if (!found) { found = [scanner scanString:@"-" intoString:&operator]; } if (!found && !scanner.isAtEnd) { // Two numbers separated by just a space if (error) { *error = MPParseError(scanner.scanLocation, @"Missing Operator"); } return nil; } else if (!found) { // Reached end of string break; } currentSummand = [self scanClosedProduct:&productClosed error:error]; // No number was found if (currentSummand == nil) { // The string ends wit + or - if (scanner.isAtEnd) { productClosed = NO; parsedElement.suffixValueExplicit = NO; NSInteger suffix = [operator isEqualToString:@"+"] ? 1 : -1; currentSummand = [[NSDecimalNumber alloc] initWithInteger:suffix]; if (error) { *error = nil; } break; // Something else instead of a number } else { if (error) { *error = MPParseError(scanner.scanLocation, @"Expected Number"); } return nil; } } // Process the current summand if ([operator isEqualToString:@"-"]) { currentSummand = [currentSummand decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:-1]]; } // The summand ends with a * if (!productClosed) { // The string ends with a * if (scanner.isAtEnd) { parsedElement.suffixValueExplicit = YES; break; // After a * followed something else than a number } else { if (error) { *error = MPParseError(scanner.scanLocation, @"Unexpected Symbol"); } return nil; } } } parsedElement.value = value; parsedElement.suffixOperatorExplicit = !productClosed; parsedElement.suffixMultiplicator = currentSummand; parsedElement.suffixIndex = scanner.scanLocation; return parsedElement; } - (NSDecimalNumber *)scanClosedProduct:(BOOL *)closed error:(MPParseError **)error { NSDecimal decimal; BOOL found = [scanner scanDecimal:&decimal]; if (!found) { if (error != NULL) { *error = MPParseError(scanner.scanLocation, @"Unexpected Symbol"); } return nil; } NSDecimalNumber *product = [NSDecimalNumber decimalNumberWithDecimal:decimal]; while (true) { found = [scanner scanString:@"*" intoString:NULL]; if (found) { found = [scanner scanDecimal:&decimal]; if (found) { product = [product decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:decimal]]; } else { if (closed != NULL) { *closed = NO; } return product; } } else { break; } } if (closed != NULL) { *closed = YES; } return product; } @end