186 lines
6.1 KiB
Objective-C
186 lines
6.1 KiB
Objective-C
//
|
|
// 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
|