Implemented Evaluation
This commit is contained in:
18
MathPad/MPElementParser.h
Normal file
18
MathPad/MPElementParser.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// MPElementParser.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 06.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MPParsedElement.h"
|
||||||
|
#import "MPParseError.h"
|
||||||
|
|
||||||
|
@interface MPElementParser : NSObject
|
||||||
|
|
||||||
|
- (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
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
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
@import Foundation;
|
@import Foundation;
|
||||||
#import "NSString+MPExpressionElement.h"
|
#import "NSString+MPExpressionElement.h"
|
||||||
|
|
||||||
@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator;
|
@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator, MPParseError;
|
||||||
@protocol MPExpressionElement;
|
@protocol MPExpressionElement;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -242,6 +242,26 @@
|
|||||||
- (NSUInteger)indexOfElementAtSymbolLocation:(NSUInteger)location offset:(out NSUInteger *)offset;
|
- (NSUInteger)indexOfElementAtSymbolLocation:(NSUInteger)location offset:(out NSUInteger *)offset;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method locationOfElementAtIndex:
|
||||||
|
@brief Calculates the location of the element at @c index.
|
||||||
|
|
||||||
|
@discussion @c index is an element index. Use this method to convert an
|
||||||
|
element index into the length reference frame.
|
||||||
|
|
||||||
|
If the index exceeds the receiver's number of elements a @c
|
||||||
|
NSRangeException will be raised.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the element that is to be converted into the length
|
||||||
|
reference frame.
|
||||||
|
|
||||||
|
@return The number of symbols (in the length reference frame) before the
|
||||||
|
element at @c index.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method replaceSymbolsInRange:withElements:
|
@method replaceSymbolsInRange:withElements:
|
||||||
@brief Replaces the elements in the given range with the contents of the
|
@brief Replaces the elements in the given range with the contents of the
|
||||||
@@ -271,28 +291,53 @@
|
|||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method elements
|
@method elements
|
||||||
@brief Returns an array of all elements in the receiver.
|
@brief Returns an array of all elements in the receiver.
|
||||||
|
|
||||||
@discussion The elements in the returned array are not copied before they are
|
@discussion The elements in the returned array are not copied before they are
|
||||||
returned.
|
returned.
|
||||||
|
|
||||||
@return An array of all elements from the receiver.
|
@return An array of all elements from the receiver.
|
||||||
*/
|
*/
|
||||||
- (NSArray *)elements;
|
- (NSArray *)elements;
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Evaluating Expressions
|
#pragma mark Evaluating Expressions
|
||||||
|
|
||||||
- (double)evaluateExpression:(NSError *__autoreleasing *)error;
|
|
||||||
|
/*!
|
||||||
|
@method evaluateWitError:
|
||||||
|
@brief Evaluates the receiving expression.
|
||||||
|
|
||||||
|
@discussion This is a convenience method for evaluating an expression. If you
|
||||||
|
want more control over the evaluation process use the @c
|
||||||
|
evaluator property of the receiver.
|
||||||
|
|
||||||
|
@param errror
|
||||||
|
If the receiver (or any of its elements) contains a syntax error
|
||||||
|
or can not be evaluated this parameter is set to an appropriate
|
||||||
|
value. Pass @c NULL if you are not interested in any errors that
|
||||||
|
might occur.
|
||||||
|
|
||||||
|
@return The result of the evaluation or @c nil of the receiver could not
|
||||||
|
be evaluated. In that case the @c error parameter is set to an
|
||||||
|
appropriate value.
|
||||||
|
*/
|
||||||
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
|
||||||
|
|
||||||
|
|
||||||
// TODO: Private?
|
/*!
|
||||||
|
@property evaluator
|
||||||
|
@brief Returns an object that can evaluate the receiver.
|
||||||
|
|
||||||
|
@discussion To just evaluate an expression it is recommended to send it an
|
||||||
|
@c evaluateWithError: message. You can however use this property
|
||||||
|
instead if you need more control over the evaluation process.
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic, strong) MPExpressionEvaluator *evaluator;
|
@property (readonly, nonatomic, strong) MPExpressionEvaluator *evaluator;
|
||||||
|
|
||||||
#pragma mark Notifications
|
#pragma mark Notifications
|
||||||
// All notification methods should create a new rangePath with the receiver's index added to the beginning of the path and then ascend the message to it's parent
|
// All notification methods should create a new rangePath with the receiver's index added to the beginning of the path and then ascend the message to it's parent
|
||||||
// TODO: More notifications
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method didChangeElementsInRangePath:replacementLength:
|
@method didChangeElementsInRangePath:replacementLength:
|
||||||
|
|||||||
@@ -221,6 +221,15 @@
|
|||||||
return elementIndex;
|
return elementIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index
|
||||||
|
{
|
||||||
|
NSUInteger location = 0;
|
||||||
|
for (NSUInteger i = 0; i < index; i++) {
|
||||||
|
location += [self elementAtIndex:i].length;
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)replaceSymbolsInRange:(NSRange)range
|
- (void)replaceSymbolsInRange:(NSRange)range
|
||||||
withElements:(NSArray *)elements
|
withElements:(NSArray *)elements
|
||||||
{
|
{
|
||||||
@@ -268,7 +277,7 @@
|
|||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (double)evaluateExpression:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return [self.evaluator evaluateWithError:error];
|
return [self.evaluator evaluateWithError:error];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
|
|
||||||
|
#import "MPParseError.h"
|
||||||
|
|
||||||
@protocol MPExpressionElement <NSObject, NSCopying, NSCoding>
|
@protocol MPExpressionElement <NSObject, NSCopying, NSCoding>
|
||||||
|
|
||||||
- (BOOL)isString;
|
- (BOOL)isString;
|
||||||
@@ -15,10 +17,6 @@
|
|||||||
|
|
||||||
- (NSUInteger)length;
|
- (NSUInteger)length;
|
||||||
|
|
||||||
- (double)doubleValue;
|
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error;
|
||||||
- (float)floatValue;
|
|
||||||
- (int)intValue;
|
|
||||||
- (NSInteger)integerValue;
|
|
||||||
- (long long)longLongValue;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,23 +8,25 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
#import "MPElementParser.h"
|
||||||
|
|
||||||
@class MPExpressionEvaluator, MPExpression, MPParsedElement;
|
@class MPExpressionEvaluator, MPExpression, MPParsedElement;
|
||||||
|
|
||||||
@interface MPExpressionEvaluator : NSObject
|
@interface MPExpressionEvaluator : NSObject
|
||||||
|
|
||||||
|
// Do not instanciate yourself, use evaluator property of MPExpression instead
|
||||||
- (instancetype)initWithExpression:(MPExpression *)expression;
|
- (instancetype)initWithExpression:(MPExpression *)expression;
|
||||||
|
|
||||||
@property (readonly, nonatomic, strong) MPExpression *expression;
|
@property (readonly, nonatomic, weak) MPExpression *expression;
|
||||||
|
|
||||||
#pragma mark Lexing
|
|
||||||
- (MPParsedElement *)structuredRepresentationOfElementAtIndex:(NSUInteger)index;
|
|
||||||
|
|
||||||
#pragma mark Evaluating Expressions
|
#pragma mark Evaluating Expressions
|
||||||
@property (readonly, nonatomic, strong) NSDictionary *variableBindings;
|
@property (readonly, nonatomic, strong) NSDictionary *variableBindings;
|
||||||
|
|
||||||
- (void)bindValue:(double)value toVariableName:(NSString *)name;
|
- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name;
|
||||||
|
- (void)unbindVariableName:(NSString *)name;
|
||||||
|
|
||||||
- (double)evaluateWithError:(NSError *__autoreleasing *)error;
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
|
||||||
|
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
|
||||||
|
@property (readonly, nonatomic, strong) NSString *definedVariable;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,11 +9,13 @@
|
|||||||
#import "MPExpressionEvaluator.h"
|
#import "MPExpressionEvaluator.h"
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
|
||||||
#import "MPParsedElement.h"
|
@interface MPExpressionEvaluator ()
|
||||||
#import "MPMath.h"
|
@property (readwrite, nonatomic, strong) NSString *definedVariable;
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation MPExpressionEvaluator {
|
@implementation MPExpressionEvaluator {
|
||||||
NSMutableDictionary *_variableBindings;
|
NSMutableDictionary *_variableBindings;
|
||||||
|
MPElementParser *parser;
|
||||||
}
|
}
|
||||||
- (id)initWithExpression:(MPExpression *)expression
|
- (id)initWithExpression:(MPExpression *)expression
|
||||||
{
|
{
|
||||||
@@ -21,36 +23,176 @@
|
|||||||
if (self) {
|
if (self) {
|
||||||
_expression = expression;
|
_expression = expression;
|
||||||
_variableBindings = [[NSMutableDictionary alloc] init];
|
_variableBindings = [[NSMutableDictionary alloc] init];
|
||||||
|
parser = [[MPElementParser alloc] init];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Lexing
|
|
||||||
- (MPParsedElement *)structuredRepresentationOfElementAtIndex:(NSUInteger)index
|
|
||||||
{
|
|
||||||
NSString *string = (NSString *)[self.expression elementAtIndex:index];
|
|
||||||
YY_BUFFER_STATE buffer = yy_scan_string(string.UTF8String);
|
|
||||||
yyparse();
|
|
||||||
yy_delete_buffer(buffer);
|
|
||||||
return [parseResult copy];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Evaluating Expressions
|
#pragma mark Evaluating Expressions
|
||||||
- (NSDictionary *)variableBindings
|
- (NSDictionary *)variableBindings
|
||||||
{
|
{
|
||||||
return [_variableBindings copy];
|
return [_variableBindings copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)bindValue:(double)value toVariableName:(NSString *)name
|
- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name
|
||||||
{
|
{
|
||||||
[_variableBindings setObject:@(value)
|
[_variableBindings setObject:value
|
||||||
forKey:name];
|
forKey:name];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (double)evaluateWithError:(NSError *__autoreleasing *)error
|
- (void)unbindVariableName:(NSString *)name
|
||||||
{
|
{
|
||||||
MPParsedElement *element = [self structuredRepresentationOfElementAtIndex:0];
|
[_variableBindings removeObjectForKey:name];
|
||||||
return element.standaloneValue;
|
}
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
||||||
|
{
|
||||||
|
return [self evaluateVariableDefinition:NO error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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");
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
if ([element isString]) {
|
||||||
|
MPParsedElement *parseResult = [parser parseElement:(NSString *)element
|
||||||
|
error:error];
|
||||||
|
if (parseResult) {
|
||||||
|
if (parseResult.definedVariable) {
|
||||||
|
if (error) {
|
||||||
|
*error = MPParseError(index, @"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 = [element evaluate:error];
|
||||||
|
if (result) {
|
||||||
|
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
#import "MPExpressionElement.h"
|
#import "MPExpressionElement.h"
|
||||||
|
#import "MPParseError.h"
|
||||||
|
|
||||||
@class MPFunction, MPExpression, MPRangePath;
|
@class MPFunction, MPExpression, MPRangePath;
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
#pragma mark Evaluating Functions
|
#pragma mark Evaluating Functions
|
||||||
- (double)evaluateFunction:(NSError *__autoreleasing *)error;
|
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error;
|
||||||
|
|
||||||
#pragma mark Messages
|
#pragma mark Messages
|
||||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||||
|
|||||||
@@ -82,9 +82,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Evaluating Functions
|
#pragma mark Evaluating Functions
|
||||||
- (double)evaluateFunction:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return 0;
|
return [NSDecimalNumber zero];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Notifications
|
#pragma mark Notifications
|
||||||
@@ -182,24 +182,4 @@
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)floatValue
|
|
||||||
{
|
|
||||||
return (float)[self doubleValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int)intValue
|
|
||||||
{
|
|
||||||
return (int)[self doubleValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)integerValue
|
|
||||||
{
|
|
||||||
return (NSInteger)[self doubleValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (long long)longLongValue
|
|
||||||
{
|
|
||||||
return (long long)[self doubleValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
21
MathPad/MPParseError.h
Normal file
21
MathPad/MPParseError.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// MPParseError.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 06.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#define MPParseError(index,key) [[MPParseError alloc] initWithErrorIndex:index errorMessageKey:key]
|
||||||
|
|
||||||
|
@interface MPParseError : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex
|
||||||
|
errorMessageKey:(NSString *)errorMessageKey;
|
||||||
|
|
||||||
|
@property (nonatomic) NSUInteger errorIndex;
|
||||||
|
@property (nonatomic) NSString *localizedErrorMessage;
|
||||||
|
|
||||||
|
@end
|
||||||
28
MathPad/MPParseError.m
Normal file
28
MathPad/MPParseError.m
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MPParseError.m
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 06.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPParseError.h"
|
||||||
|
|
||||||
|
@implementation MPParseError
|
||||||
|
|
||||||
|
- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex errorMessageKey:(NSString *)errorMessageKey
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_errorIndex = errorIndex;
|
||||||
|
_localizedErrorMessage = NSLocalizedString(errorMessageKey, nil);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"MPParseError<at=%ld message=\"%@\">", self.errorIndex, self.localizedErrorMessage];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -8,32 +8,37 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
// DEBUG STUFF
|
#import "MPParseError.h"
|
||||||
// Simple Operator: +/-/*
|
|
||||||
// Simple Factor: * 5.4 * / * 5,4 / 5,4 * / 5,4
|
|
||||||
// Independant Math: -1+2-3
|
|
||||||
|
|
||||||
@interface MPParsedElement : NSObject <NSCopying>
|
@interface MPParsedElement : NSObject <NSCopying>
|
||||||
|
|
||||||
|
@property (nonatomic, copy) NSString *definedVariable;
|
||||||
|
@property (nonatomic) NSUInteger afterVariableDefinitionIndex; // Only set if defineVariable != nil
|
||||||
|
|
||||||
@property (nonatomic) BOOL isFactor;
|
@property (nonatomic) BOOL isFactor;
|
||||||
@property (nonatomic) double factor;
|
|
||||||
|
|
||||||
@property (nonatomic, strong) NSMutableArray *summands;
|
// If isFactor is YES this is the factor otherwise it is the independant summant
|
||||||
|
// of the element (may be 0).
|
||||||
|
@property (nonatomic) NSDecimalNumber *value;
|
||||||
|
|
||||||
@property (nonatomic) double prefixMultiplicator;
|
|
||||||
@property (nonatomic) BOOL hasPrefixMultiplicator;
|
|
||||||
@property (nonatomic, getter = isPrefixOperatorExplicit) BOOL prefixOperatorExplicit;
|
@property (nonatomic, getter = isPrefixOperatorExplicit) BOOL prefixOperatorExplicit;
|
||||||
@property (nonatomic) double suffixMultiplicator;
|
@property (nonatomic, getter = isPrefixValueExplicit) BOOL prefixValueExplicit;
|
||||||
@property (nonatomic) BOOL hasSuffixMultiplicator;
|
@property (nonatomic) NSDecimalNumber *prefixMultiplicator;
|
||||||
|
|
||||||
// No error checking done. The string parsed may for example end with an operator
|
@property (nonatomic, getter = isSuffixOperatorExplicit) BOOL suffixOperatorExplicit;
|
||||||
- (double)valueAtBeginning;
|
@property (nonatomic, getter = isSuffixValueExplicit) BOOL suffixValueExplicit;
|
||||||
- (double)valueAtEnd;
|
@property (nonatomic) NSDecimalNumber *suffixMultiplicator;
|
||||||
- (double)standaloneValue;
|
@property (nonatomic) NSUInteger suffixIndex;
|
||||||
|
|
||||||
|
// No error checking done
|
||||||
|
- (NSDecimalNumber *)valueAtBeginning;
|
||||||
|
- (NSDecimalNumber *)valueAtEnd;
|
||||||
|
- (NSDecimalNumber *)standaloneValue;
|
||||||
|
|
||||||
// For error checking
|
// For error checking
|
||||||
- (BOOL)isValidElementAtBeginning;
|
- (BOOL)isValidElementAtBeginning:(MPParseError **)error;
|
||||||
- (BOOL)isValidElementInBetween;
|
- (BOOL)isValidStandaloneElement:(MPParseError **)error;
|
||||||
- (BOOL)isValidElementAtEnd;
|
- (BOOL)isValidElementAtEnd:(MPParseError **)error;
|
||||||
|
- (BOOL)isValidVariableDefinition:(MPParseError **)error;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -14,46 +14,109 @@
|
|||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_summands = [[NSMutableArray alloc] init];
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (double)standaloneValue
|
- (NSDecimalNumber *)valueAtBeginning
|
||||||
|
{
|
||||||
|
NSDecimalNumber *value = self.value;
|
||||||
|
if (self.prefixValueExplicit) {
|
||||||
|
value = [value decimalNumberByAdding:self.prefixMultiplicator];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)valueAtEnd
|
||||||
|
{
|
||||||
|
NSDecimalNumber *value = self.value;
|
||||||
|
if (self.suffixValueExplicit) {
|
||||||
|
value = [value decimalNumberByAdding:self.suffixMultiplicator];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)standaloneValue
|
||||||
{
|
{
|
||||||
if (self.isFactor) {
|
if (self.isFactor) {
|
||||||
return self.factor;
|
return self.value;
|
||||||
}
|
}
|
||||||
return self.prefixMultiplicator + [[self.summands valueForKeyPath:@"@sum.self"] doubleValue];
|
NSDecimalNumber *value = self.value;
|
||||||
|
if (self.prefixValueExplicit) {
|
||||||
|
value = [value decimalNumberByAdding:self.prefixMultiplicator];
|
||||||
|
}
|
||||||
|
if (self.suffixValueExplicit) {
|
||||||
|
value = [value decimalNumberByAdding:self.suffixMultiplicator];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isValidElementAtBeginning
|
- (BOOL)isValidElementAtBeginning:(MPParseError *__autoreleasing *)error
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isValidElementInBetween
|
|
||||||
{
|
{
|
||||||
|
if (self.prefixOperatorExplicit) {
|
||||||
|
if (error) {
|
||||||
|
*error = MPParseError(0, @"Expected Number");
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isValidElementAtEnd
|
- (BOOL)isValidStandaloneElement:(MPParseError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return NO;
|
return [self isValidElementAtBeginning:error] && [self isValidElementAtEnd:error];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark NSCopying
|
- (BOOL)isValidElementAtEnd:(MPParseError *__autoreleasing *)error
|
||||||
|
{
|
||||||
|
if (self.suffixOperatorExplicit) {
|
||||||
|
if (error) {
|
||||||
|
*error = MPParseError(self.suffixIndex, @"Expected Number");
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValidVariableDefinition:(MPParseError *__autoreleasing *)error
|
||||||
|
{
|
||||||
|
if (self.definedVariable == nil) {
|
||||||
|
if (error) {
|
||||||
|
*error = MPParseError(0, @"Expected Variable Definition");
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return [self isValidElementAtBeginning:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSObject Overrides
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
NSMutableString *description = [[NSMutableString alloc] initWithString:@"MPParsedElement<"];
|
||||||
|
if (self.isFactor) {
|
||||||
|
[description appendFormat:@"factor=%@%@%@", self.prefixOperatorExplicit?@"*":@"", self.value, self.suffixOperatorExplicit?@"*":@""];
|
||||||
|
} else {
|
||||||
|
[description appendFormat:@"prefix=%@%@ prefixValueExplicit=%@", self.prefixOperatorExplicit?@"*":@"", self.prefixMultiplicator, self.prefixValueExplicit?@"YES":@"NO"];
|
||||||
|
[description appendFormat:@" suffix=%@%@ suffixValueExplicit=%@", self.suffixMultiplicator, self.suffixOperatorExplicit?@"*":@"", self.suffixValueExplicit?@"YES":@"NO"];
|
||||||
|
[description appendFormat:@" value=%@", self.value];
|
||||||
|
}
|
||||||
|
[description appendString:@">"];
|
||||||
|
return [description copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSCopying
|
||||||
- (id)copyWithZone:(NSZone *)zone
|
- (id)copyWithZone:(NSZone *)zone
|
||||||
{
|
{
|
||||||
MPParsedElement *copy = [[MPParsedElement allocWithZone:zone] init];
|
MPParsedElement *copy = [[MPParsedElement allocWithZone:zone] init];
|
||||||
copy.isFactor = self.isFactor;
|
copy.isFactor = self.isFactor;
|
||||||
copy.factor = self.factor;
|
copy.value = self.value;
|
||||||
copy.summands = self.summands.mutableCopy;
|
|
||||||
copy.prefixMultiplicator = self.prefixMultiplicator;
|
|
||||||
copy.hasPrefixMultiplicator = self.hasPrefixMultiplicator;
|
|
||||||
copy.prefixOperatorExplicit = self.prefixOperatorExplicit;
|
copy.prefixOperatorExplicit = self.prefixOperatorExplicit;
|
||||||
|
copy.prefixValueExplicit = self.prefixValueExplicit;
|
||||||
|
copy.prefixMultiplicator = self.prefixMultiplicator;
|
||||||
|
copy.suffixOperatorExplicit = self.suffixOperatorExplicit;
|
||||||
|
copy.suffixValueExplicit = self.suffixValueExplicit;
|
||||||
copy.suffixMultiplicator = self.suffixMultiplicator;
|
copy.suffixMultiplicator = self.suffixMultiplicator;
|
||||||
copy.hasSuffixMultiplicator = self.hasSuffixMultiplicator;
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user