Archived
1

Implemented Expression Evaluation/Parsing with Proper Error Handling

This commit is contained in:
Kim Wittenburg
2014-09-11 22:17:29 +02:00
parent f791213127
commit 245468a559
16 changed files with 670 additions and 330 deletions

View File

@@ -2,17 +2,36 @@
// MPElementParser.h
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPParsedElement.h"
#import "MPParsedProduct.h"
#import "MPParseError.h"
#define MPMaximumOperatorChainLength NSUIntegerMax
@interface MPElementParser : NSObject
- (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error;
+ (BOOL)isUsingDefaultValuesFromUserDefaults;
+ (void)setUsingDefaultValuesFromUserDefaults:(BOOL)flag;
@property (nonatomic) BOOL allowsImplicitMultiplications; // wether 2 3 is equal to 6 or error
// Default value uses the key "..." in NSUserDefaults or ... if the key does not exist.
@property (nonatomic) NSUInteger maximumOperatorChainLength; // +--++-5 -> Chain length: 6, 2 default (sign of number and operator) (0 is invalid?)
@property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0
@property (nonatomic) NSUInteger maximumOperatorChainLengthInFunction; // For sin, cos, tan. Default: 1
- (NSArray *)parseElement:(NSString *)string
previousProduct:(MPParsedProduct *)previousProduct
nextFactor:(MPParsedFactor *)nextFactor
definesVariable:(BOOL)flag
definedVariable:(NSString *__autoreleasing *)variableName
error:(out MPParseError *__autoreleasing *)error;
- (NSArray *)parseElement:(NSString *)string
previousProduct:(MPParsedProduct *)previousProduct
nextFactor:(MPParsedFactor *)nextFactor
error:(out MPParseError *__autoreleasing *)error;
@end

View File

@@ -2,184 +2,335 @@
// MPElementParser.m
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPElementParser.h"
#import "NSRegularExpression+MPParsingAdditions.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;
- (NSRange)parseOperators;
- (NSUInteger)countOperatorsInRange:(NSRange)range
multiplicator:(out NSDecimalNumber *__autoreleasing *)multiplicator;
- (NSRange)parseNumber;
- (NSDecimalNumber *)numberInRange:(NSRange)range;
@end
@implementation MPElementParser {
NSScanner *scanner;
MPParseError *__autoreleasing *outError;
}
- (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) {
static BOOL useUserDefaults;
// 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");
}
#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:(MPParsedFactor *)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:(MPParsedFactor *)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 (!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");
} else if (variableName) {
*variableName = definedVariable;
}
} else if (definedVariable) {
self.error = MPParseError(variableDefinitionRange, @"Unexpected Variable Definition");
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;
NSMutableArray *products = [[NSMutableArray alloc] init];
MPParsedProduct *currentProduct = previousProduct;
while (![self isAtEnd]) {
[self parseWhitespaces];
NSRange multiplicationSymbolRange = [self parseMultiplicationSymbol];
BOOL hasMultiplicationSymbol = multiplicationSymbolRange.location != NSNotFound;
NSRange operatorsRange = [self parseOperators];
BOOL hasOperators = operatorsRange.location != NSNotFound;
// NSRange functionRange = ...
// BOOL hasFunction = functionRange.location != NSNotFound;
NSRange numberRange = [self parseNumber];
BOOL hasNumber = numberRange.location != NSNotFound;
NSDecimalNumber *operatorMultiplicator;
NSUInteger operatorCount = [self countOperatorsInRange:operatorsRange
multiplicator:&operatorMultiplicator];
if (!hasNumber) {
if ([self isAtEnd] && nextFactor != nil) {
if (hasMultiplicationSymbol) {
if (operatorCount > self.maximumOperatorChainLengthInFunction) {
self.error = MPParseError(operatorsRange, @"Too many operators in multiplication.");
return nil;
}
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:operatorMultiplicator]];
} else if (hasOperators) {
if (operatorCount > self.maximumOperatorChainLength) {
self.error = MPParseError(operatorsRange, @"Too many operators.");
return nil;
}
[products addObject:currentProduct];
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:[[NSDecimalNumber alloc] initWithUnsignedInteger:operatorCount]]];
} else if (self.allowsImplicitMultiplications) {
if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] init];
}
} else {
self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Implicit Multiplication not allowed.");
return nil;
}
return product;
break;
} else if ([self isAtEnd]) {
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;
}
} else {
break;
NSDecimalNumber *number = [self numberInRange:numberRange];
NSDecimalNumber *value = [operatorMultiplicator decimalNumberByMultiplyingBy:number];
if (hasMultiplicationSymbol) {
if (currentProduct) {
if (operatorCount > self.maximumOperatorChainLengthInMultiplication) {
self.error = MPParseError(operatorsRange, @"Too many operators in multiplication.");
return nil;
}
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else {
self.error = MPParseError(multiplicationSymbolRange, @"Unexpected Symbol. Expected Number.");
return nil;
}
} else if (hasOperators) {
if (operatorCount > self.maximumOperatorChainLength) {
self.error = MPParseError(operatorsRange, @"Too many operators.");
return nil;
}
if (currentProduct) {
[products addObject:currentProduct];
}
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else if (self.allowsImplicitMultiplications) {
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else {
self.error = MPParseError(NSMakeRange(numberRange.location, 0), @"Implicit Multiplication not allowed.");
return nil;
}
}
}
if (closed != NULL) {
*closed = YES;
if (nextFactor) {
[currentProduct addFactor:nextFactor];
}
return product;
[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;
- (NSRange)parseOperators
{
if (!operatorsRegex) {
operatorsRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([+-](?:\\s*[+-])*)\\s*"
options:0
error:NULL];
}
NSTextCheckingResult *match = [operatorsRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
if (match == nil) {
return NSMakeRange(NSNotFound, 0);
}
self.parsePosition = NSMaxRange(match.range);
return [match rangeAtIndex:1];
}
- (NSUInteger)countOperatorsInRange:(NSRange)range
multiplicator:(out NSDecimalNumber *__autoreleasing *)outMultiplicator
{
if (range.location == NSNotFound) {
*outMultiplicator = [NSDecimalNumber one];
return 0;
}
NSString *operatorsString = [self.input substringWithRange:range];
NSString *operators = [[operatorsString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
NSInteger multiplicator = 1;
for (NSUInteger characterIndex; characterIndex < operators.length; characterIndex++) {
if ([[operators substringWithRange:NSMakeRange(characterIndex, 1)] isEqualToString:@"-"]) {
multiplicator *= -1;
}
}
*outMultiplicator = [[NSDecimalNumber alloc] initWithInteger:multiplicator];
return operators.length;
}
- (NSRange)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];
if (!match) {
return NSMakeRange(NSNotFound, 0);
}
self.parsePosition = NSMaxRange(match.range);
return [match rangeAtIndex:1];
}
- (NSDecimalNumber *)numberInRange:(NSRange)range
{
NSString *numberString = [self.input substringWithRange:range];
return [NSDecimalNumber decimalNumberWithString:numberString];
}
@end

View File

@@ -17,6 +17,4 @@
- (NSUInteger)length;
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error;
@end

View File

@@ -10,7 +10,7 @@
#import "MPExpression.h"
#import "MPElementParser.h"
@class MPExpressionEvaluator, MPExpression, MPParsedElement;
@class MPExpressionEvaluator, MPExpression, MPParsedElementOld;
@interface MPExpressionEvaluator : NSObject
@@ -25,8 +25,9 @@
- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name;
- (void)unbindVariableName:(NSString *)name;
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
@property (readonly, nonatomic, strong) NSString *definedVariable;
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
@end

View File

@@ -8,9 +8,11 @@
#import "MPExpressionEvaluator.h"
#import "MPExpression.h"
#import "MPFunction.h"
@interface MPExpressionEvaluator ()
@property (readwrite, nonatomic, strong) NSString *definedVariable;
@end
@implementation MPExpressionEvaluator {
@@ -52,146 +54,74 @@
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error
{
#warning Implement Cache
// Empty Expression
if (self.expression.numberOfElements == 0) {
if (error) {
*error = MPParseError(0, @"Empty Expression");
}
*error = MPParseError(NSMakeRange(0, 0), @"Expected Expression");
return nil;
}
// Expression of just one element
if (self.expression.numberOfElements == 1) {
id<MPExpressionElement> singleElement = [self.expression elementAtIndex:0];
if ([singleElement isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)singleElement
error:error];
if ([parseResult isValidStandaloneElement:error]) {
if (flag && parseResult.definedVariable) {
self.definedVariable = parseResult.definedVariable;
} else if (flag) {
if (error) {
*error = MPParseError(0, @"Expected Variable Definition");
}
return nil;
} else if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(0, @"Unexpected Variable Definition");
}
return nil;
}
return parseResult.standaloneValue;
} else {
return nil;
}
} else {
return [singleElement evaluate:error];
}
}
// Expression with any number of elements
NSDecimalNumber *value = [NSDecimalNumber zero];
NSDecimalNumber *currentSummand = nil;
NSMutableArray *products = [[NSMutableArray alloc] init];
MPParsedProduct *currentProduct = nil;
// Process the first element
id<MPExpressionElement> firstElement = [self.expression elementAtIndex:0];
if ([firstElement isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)firstElement
error:error];
if (parseResult && [parseResult isValidElementAtBeginning:error]) {
if (flag && parseResult.definedVariable) {
self.definedVariable = parseResult.definedVariable;
} else if (flag) {
if (error) {
*error = MPParseError(0, @"Expected Variable Definition");
}
return nil;
} else if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(0, @"Unexpected Variable Definition");
}
return nil;
}
if ([parseResult isFactor]) {
currentSummand = parseResult.value;
} else {
value = [parseResult valueAtBeginning];
currentSummand = parseResult.suffixMultiplicator;
}
}
} else {
currentSummand = [firstElement evaluate:error];
}
if (!currentSummand) {
return nil;
}
// Process the elements between the first and last element
for (NSUInteger index = 1; index < self.expression.numberOfElements-1; index++) {
id<MPExpressionElement> element = [self.expression elementAtIndex:index];
for (NSUInteger elementIndex = 0; elementIndex < self.expression.numberOfElements; elementIndex++) {
id<MPExpressionElement> element = [self.expression elementAtIndex:elementIndex];
if ([element isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)element
error:error];
if (parseResult) {
if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(index, @"Unexpected Variable Definition");
}
MPParsedFactor *nextFactor = nil;
if (elementIndex < self.expression.numberOfElements - 1) {
MPFunction *nextFunction = (MPFunction *)[self.expression elementAtIndex:elementIndex+1];
NSDecimalNumber *functionValue = [nextFunction evaluate:error];
if (!functionValue) {
return nil;
}
if ([parseResult isFactor]) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
} else {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
currentSummand = parseResult.suffixMultiplicator;
}
nextFactor = [MPParsedFactor factorWithDecimalNumber:functionValue];
}
NSArray *newProducts;
if (elementIndex == 0 && flag) {
NSString *definedVariable;
newProducts = [parser parseElement:(NSString *)element
previousProduct:currentProduct
nextFactor:nextFactor
definesVariable:YES
definedVariable:&definedVariable
error:error];
self.definedVariable = definedVariable;
} else {
newProducts = [parser parseElement:(NSString *)element
previousProduct:currentProduct
nextFactor:nextFactor
error:error];
}
if (!newProducts) {
return nil;
}
for (NSUInteger productIndex = 0; productIndex < newProducts.count-1; productIndex++) {
[products addObject:newProducts[productIndex]];
}
currentProduct = newProducts.lastObject;
elementIndex++;
} else {
NSDecimalNumber *result = [element evaluate:error];
if (result) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
} else {
NSDecimalNumber *functionValue = [(MPFunction *)element evaluate:error];
if (!functionValue) {
return nil;
}
if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] init];
}
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:functionValue]];
}
}
// Process the last element
id<MPExpressionElement> lastElement = [self.expression elementAtIndex:self.expression.numberOfElements-1];
if ([lastElement isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)lastElement
error:error];
if (parseResult && [parseResult isValidElementAtEnd:error]) {
if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(0, @"Unexpected Variable Definition");
}
return nil;
}
if ([parseResult isFactor]) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
} else {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
currentSummand = parseResult.suffixMultiplicator;
}
} else {
return nil;
}
} else {
NSDecimalNumber *result = [lastElement evaluate:error];
if (result) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
} else {
return nil;
}
}
value = [value decimalNumberByAdding:currentSummand];
[products addObject:currentProduct];
NSDecimalNumber *value = [NSDecimalNumber zero];
for (MPParsedProduct *product in products) {
value = [value decimalNumberByAdding:product.value];
}
return value;
}

View File

@@ -2,20 +2,25 @@
// MPParseError.h
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Created by Kim Wittenburg on 08.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#define MPParseError(index,key) [[MPParseError alloc] initWithErrorIndex:index errorMessageKey:key]
#define MPParseError(range,key) [[MPParseError alloc] initWithErrorRange:range errorMessageKey:key]
@interface MPParseError : NSObject
- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex
errorMessageKey:(NSString *)errorMessageKey;
- (instancetype)initWithErrorRange:(NSRange)errorRange
errorMessageKey:(NSString *)key;
@property (nonatomic) NSUInteger errorIndex;
@property (nonatomic) NSString *localizedErrorMessage;
- (instancetype)initWithErrorRange:(NSRange)errorRange
localizederrorMessage:(NSString *)errorMessage;
@end
@property (nonatomic, strong) NSIndexPath *pathToExpression;
@property (nonatomic) NSRange errorRange;
@property (nonatomic, copy) NSString *localizedErrorMessage;
@end

View File

@@ -2,7 +2,7 @@
// MPParseError.m
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Created by Kim Wittenburg on 08.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@@ -10,19 +10,29 @@
@implementation MPParseError
- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex errorMessageKey:(NSString *)errorMessageKey
- (instancetype)initWithErrorRange:(NSRange)errorRange errorMessageKey:(NSString *)key
{
self = [super init];
if (self) {
_errorIndex = errorIndex;
_localizedErrorMessage = NSLocalizedString(errorMessageKey, nil);
_errorRange = errorRange;
_localizedErrorMessage = NSLocalizedString(key, nil);
}
return self;
}
- (instancetype)initWithErrorRange:(NSRange)errorRange localizederrorMessage:(NSString *)errorMessage
{
self = [super init];
if (self) {
_errorRange = errorRange;
_localizedErrorMessage = errorMessage;
}
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"MPParseError<at=%ld message=\"%@\">", self.errorIndex, self.localizedErrorMessage];
return [NSString stringWithFormat:@"MPParseError<at=(%ld, %ld) message=\"%@\">", self.errorRange.location, self.errorRange.length, self.localizedErrorMessage];
}
@end

22
MathPad/MPParsedFactor.h Normal file
View File

@@ -0,0 +1,22 @@
//
// MPParsedFactor.h
// MathPad
//
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MPParsedFactor : NSObject
+ (MPParsedFactor *)factorWithDecimalNumber:(NSDecimalNumber *)number;
+ (MPParsedFactor *)sinFactorWithFactor:(MPParsedFactor *)factor;
+ (MPParsedFactor *)cosFactorWithFactor:(MPParsedFactor *)factor;
+ (MPParsedFactor *)tanFactorWithFactor:(MPParsedFactor *)factor;
- (instancetype)initWithDecimalNumber:(NSDecimalNumber *)number;
- (NSDecimalNumber *)value;
@end

60
MathPad/MPParsedFactor.m Normal file
View File

@@ -0,0 +1,60 @@
//
// MPParsedFactor.m
// MathPad
//
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPParsedFactor.h"
@implementation MPParsedFactor {
NSDecimalNumber *_value;
}
+ (MPParsedFactor *)factorWithDecimalNumber:(NSDecimalNumber *)number
{
return [[MPParsedFactor alloc] initWithDecimalNumber:number];
}
+ (MPParsedFactor *)sinFactorWithFactor:(MPParsedFactor *)factor
{
double value = factor.value.doubleValue;
NSDecimalNumber *actualNumber = [[NSDecimalNumber alloc] initWithDouble:sin(value)];
return [[MPParsedFactor alloc] initWithDecimalNumber:actualNumber];
}
+ (MPParsedFactor *)cosFactorWithFactor:(MPParsedFactor *)factor
{
double value = factor.value.doubleValue;
NSDecimalNumber *actualNumber = [[NSDecimalNumber alloc] initWithDouble:cos(value)];
return [[MPParsedFactor alloc] initWithDecimalNumber:actualNumber];
}
+ (MPParsedFactor *)tanFactorWithFactor:(MPParsedFactor *)factor
{
double value = factor.value.doubleValue;
NSDecimalNumber *actualNumber = [[NSDecimalNumber alloc] initWithDouble:tan(value)];
return [[MPParsedFactor alloc] initWithDecimalNumber:actualNumber];
}
- (instancetype)initWithDecimalNumber:(NSDecimalNumber *)number
{
self = [super init];
if (self) {
_value = number;
}
return self;
}
- (NSDecimalNumber *)value
{
return _value;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"MPParsedFactor<%@>", _value];
}
@end

20
MathPad/MPParsedProduct.h Normal file
View File

@@ -0,0 +1,20 @@
//
// MPParsedSummand.h
// MathPad
//
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPParsedFactor.h"
@interface MPParsedProduct : NSObject
@property (readonly, nonatomic, strong) NSArray *factors;
- (void)addFactor:(MPParsedFactor *)factor;
- (NSDecimalNumber *)value;
@end

51
MathPad/MPParsedProduct.m Normal file
View File

@@ -0,0 +1,51 @@
//
// MPParsedSummand.m
// MathPad
//
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPParsedProduct.h"
@implementation MPParsedProduct {
NSMutableArray *_factors;
}
- (instancetype)init
{
self = [super init];
if (self) {
_factors = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray *)factors
{
return _factors;
}
- (void)addFactor:(MPParsedFactor *)factor
{
[_factors addObject:factor];
}
- (NSDecimalNumber *)value
{
if (_factors.count == 0) {
return [NSDecimalNumber zero];
}
NSDecimalNumber *value = [NSDecimalNumber one];
for (MPParsedFactor *factor in _factors) {
value = [value decimalNumberByMultiplyingBy:factor.value];
}
return value;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"MPParsedProduct<%@>", [_factors componentsJoinedByString:@"*"]];
}
@end

View File

@@ -33,6 +33,14 @@
return 2;
}
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{
if (index != 2) {
return 2;
}
return [super indexOfChildAfterChildAtIndex:index];
}
- (CTLineRef)line
{
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{

View File

@@ -0,0 +1,21 @@
//
// NSRegularExpression+MPParsingAdditions.h
// MathPad
//
// Created by Kim Wittenburg on 09.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#define MPStringRange(string) NSMakeRange(0, [string length])
#define MPStringRangeFrom(from, string) NSMakeRange(from, [string length]-from)
@interface NSRegularExpression (MPParsingAdditions)
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string;
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string fromIndex:(NSUInteger)start;
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options;
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options fromIndex:(NSUInteger)start;
@end

View File

@@ -0,0 +1,33 @@
//
// NSRegularExpression+MPParsingAdditions.m
// MathPad
//
// Created by Kim Wittenburg on 09.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "NSRegularExpression+MPParsingAdditions.h"
@implementation NSRegularExpression (MPParsingAdditions)
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string
{
return [self firstMatchInString:string options:0 range:MPStringRange(string)];
}
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string fromIndex:(NSUInteger)start
{
return [self firstMatchInString:string options:0 range:NSMakeRange(start, [string length]-start)];
}
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options
{
return [self firstMatchInString:string options:options range:MPStringRange(string)];
}
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options fromIndex:(NSUInteger)start
{
return [self firstMatchInString:string options:options range:NSMakeRange(start, [string length]-start)];
}
@end

View File

@@ -21,9 +21,4 @@
return NO;
}
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error
{
return [[[MPElementParser alloc] init] parseElement:self error:error].standaloneValue;
}
@end