Implemented Evaluation
This commit is contained in:
185
MathPad/MPElementParser.m
Normal file
185
MathPad/MPElementParser.m
Normal file
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user