Archived
1

Improved Evaluation

This commit is contained in:
Kim Wittenburg
2014-09-28 23:50:18 +02:00
parent 43b6f78afb
commit d67a1949e9
19 changed files with 841 additions and 89 deletions

View File

@@ -15,9 +15,10 @@
- (void)push; - (void)push;
- (void)pop; - (void)pop;
- (void)bindValue:(NSDecimalNumber *)value toName:(NSString *)variableName; - (void)defineVariable:(NSString *)variable withValue:(id)value;
- (void)unbindVariableName:(NSString *)variableName; - (void)undefineVariable:(NSString *)variable;
- (BOOL)isVariableDefined:(NSString *)variable;
- (NSDecimalNumber *)valueForVariableName:(NSString *)variableName; - (NSDecimalNumber *)valueForVariable:(NSString *)variable;
@end @end

View File

@@ -45,22 +45,27 @@ static MPEvaluationContext *sharedContext;
[self.stack removeLastObject]; [self.stack removeLastObject];
} }
- (void)bindValue:(NSDecimalNumber *)value toName:(NSString *)variableName - (void)defineVariable:(NSString *)variable withValue:(id)value
{ {
NSMutableDictionary *currentBindings = self.stack.lastObject; NSMutableDictionary *currentBindings = self.stack.lastObject;
currentBindings[variableName] = value; currentBindings[variable] = value;
} }
- (void)unbindVariableName:(NSString *)variableName - (void)undefineVariable:(NSString *)variable
{ {
NSMutableDictionary *currentBindings = self.stack.lastObject; NSMutableDictionary *currentBindings = self.stack.lastObject;
[currentBindings removeObjectForKey:variableName]; [currentBindings removeObjectForKey:variable];
} }
- (NSDecimalNumber *)valueForVariableName:(NSString *)variableName - (NSDecimalNumber *)valueForVariable:(NSString *)variable
{ {
NSMutableDictionary *currentBindings = self.stack.lastObject; NSMutableDictionary *currentBindings = self.stack.lastObject;
return currentBindings[variableName]; return currentBindings[variable];
}
- (BOOL)isVariableDefined:(NSString *)variable
{
return [self valueForVariable:variable] != nil;
} }
@end @end

View File

@@ -258,7 +258,9 @@
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error - (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
{ {
return [self.evaluator evaluateWithError:error]; MPTerm *term = [self.evaluator parseExpectingVariable:NO
error:error];
return [term evaluate];
} }
@synthesize evaluator = _evaluator; @synthesize evaluator = _evaluator;

View File

@@ -8,7 +8,8 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "MPExpression.h" #import "MPExpression.h"
#import "MPElementParser.h" #import "MPExpressionTokenizer.h"
#import "MPTerm.h"
@class MPExpressionEvaluator, MPExpression, MPParsedElementOld; @class MPExpressionEvaluator, MPExpression, MPParsedElementOld;
@@ -18,10 +19,14 @@
- (instancetype)initWithExpression:(MPExpression *)expression; - (instancetype)initWithExpression:(MPExpression *)expression;
@property (readonly, nonatomic, weak) MPExpression *expression; @property (readonly, nonatomic, weak) MPExpression *expression;
#pragma mark Evaluating Expressions @property (nonatomic, strong) MPExpressionTokenizer *lexer;
@property (readonly, nonatomic, strong) NSString *definedVariable;
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error; - (void)expressionDidChangeInRange:(NSRange)range
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error; replacementLength:(NSUInteger)replacementLength;
#pragma mark Evaluating Expressions
@property (readonly, nonatomic, copy) NSString *definedVariable;
- (MPTerm *)parseExpectingVariable:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
@end @end

View File

@@ -9,105 +9,236 @@
#import "MPExpressionEvaluator.h" #import "MPExpressionEvaluator.h"
#import "MPExpression.h" #import "MPExpression.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPParsedFactor.h"
#import "MPFunction+MPParsedFactor.h"
#import "MPMathRules.h" #import "MPMathRules.h"
#import "MPToken.h"
#import "MPFunction+MPToken.h"
#import "MPTokenStream.h"
#import "MPEvaluationContext.h"
@interface MPExpressionEvaluator () @interface MPExpressionEvaluator ()
@property (readwrite, nonatomic, strong) NSString *definedVariable; @property (readwrite, nonatomic, copy) NSString *definedVariable;
@property (nonatomic, strong) NSArray *tokens;
- (void)setError:(MPParseError *)error;
@end @end
@implementation MPExpressionEvaluator { @implementation MPExpressionEvaluator {
MPElementParser *parser; MPParseError *__autoreleasing *_error;
MPTokenStream *tokenStream;
} }
- (id)initWithExpression:(MPExpression *)expression - (id)initWithExpression:(MPExpression *)expression
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
_expression = expression; _expression = expression;
parser = [[MPElementParser alloc] init];
} }
return self; return self;
} }
#pragma mark Evaluating Expressions @synthesize lexer = _lexer;
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
- (void)setLexer:(MPExpressionTokenizer *)lexer
{ {
return [self evaluateVariableDefinition:NO error:error]; _lexer = lexer;
self.tokens = nil;
} }
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error - (MPExpressionTokenizer *)lexer
{ {
// Empty Expression if (!_lexer) {
if (self.expression.numberOfElements == 0) { _lexer = [[MPExpressionTokenizer alloc] init];
*error = MPParseError(NSMakeRange(0, 0), @"Expected Expression"); }
return _lexer;
}
- (NSArray *)tokens
{
if (!_tokens) {
_tokens = [self.lexer tokenizeExpression:self.expression];
}
return _tokens;
}
- (void)expressionDidChangeInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength
{
self.tokens = nil;
}
#pragma mark Evaluating Expressions
- (void)setError:(MPParseError *)error
{
if (_error) {
error.pathToExpression = self.expression.indexPath;
*_error = error;
}
}
- (MPTerm *)parseExpectingVariable:(BOOL)flag
error:(MPParseError *__autoreleasing *)error
{
_error = error;
tokenStream = [[MPTokenStream alloc] initWithTokens:self.tokens];
if (!tokenStream.hasMoreTokens) {
self.error = MPParseError(NSMakeRange(0, 0), @"Empty Expression");
return nil; return nil;
} }
NSMutableArray *products = [[NSMutableArray alloc] init];
MPParsedProduct *currentProduct = nil;
for (NSUInteger elementIndex = 0; elementIndex < self.expression.numberOfElements; elementIndex++) { // Variable Definition
if (flag) {
MPToken *variableToken = [tokenStream nextToken];
MPToken *equalsToken = [tokenStream nextTokenOfType:MPEqualsToken];
id<MPExpressionElement> element = [self.expression elementAtIndex:elementIndex]; if (variableToken.tokenType != MPVariableToken) {
self.error = MPParseError(variableToken.range, @"Expected Variable Definition.");
if ([element isString]) { return nil;
id<MPParsedFactor> nextFactor = nil; }
if (elementIndex < self.expression.numberOfElements - 1) { if (equalsToken.tokenType != MPEqualsToken) {
MPFunction *nextFunction = (MPFunction *)[self.expression elementAtIndex:elementIndex+1]; self.error = MPParseError(equalsToken.range, @"Expected \"=\".");
nextFactor = nextFunction; return nil;
} }
self.definedVariable = variableToken.stringValue;
NSArray *newProducts; }
if (elementIndex == 0 && flag) {
NSString *definedVariable; BOOL isAtBeginning = YES;
newProducts = [parser parseElement:(NSString *)element NSMutableArray *summands = [[NSMutableArray alloc] init];
previousProduct:currentProduct NSMutableArray *currentSummand = [[NSMutableArray alloc] init];
nextFactor:nextFactor
definesVariable:YES while (tokenStream.hasMoreTokens) {
definedVariable:&definedVariable MPToken *multiplicationToken = [tokenStream nextTokenOfType:MPMultiplicationSymbolToken];
error:error]; MPToken *operatorToken = [tokenStream nextTokenOfType:MPOperatorListToken];
self.definedVariable = definedVariable; NSRange valueRange;
} else { MPTerm *value = [self nextValue:&valueRange];
newProducts = [parser parseElement:(NSString *)element
previousProduct:currentProduct if (multiplicationToken) {
nextFactor:nextFactor if (isAtBeginning) {
error:error]; self.error = MPParseError(multiplicationToken.range, @"Unexpected Symbol. Expected Number.");
} return nil;
if (!newProducts) { }
return nil; if (operatorToken) {
} if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) {
self.error = MPParseError(operatorToken.range, @"Too many operators in multiplication");
for (NSUInteger productIndex = 0; productIndex < newProducts.count-1; productIndex++) { return nil;
[products addObject:newProducts[productIndex]]; }
} [currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
currentProduct = newProducts.lastObject; }
if (value) {
elementIndex++; [currentSummand addObject:value];
} else { } else {
if (!currentProduct) { return nil;
currentProduct = [[MPParsedProduct alloc] init]; }
} else if ([MPMathRules sharedRules].allowsImplicitMultiplication) { } else {
[currentProduct addFactor:(MPFunction *)element]; if (operatorToken) {
} else { if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLength]) {
*error = MPParseError(NSMakeRange(((MPFunction *)element).range.location, 0), @"Implicit Multiplication not allowed"); self.error = MPParseError(operatorToken.range, @"Too many operators.");
return nil; return nil;
} }
} [summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
} currentSummand = [[NSMutableArray alloc] init];
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
[products addObject:currentProduct]; if (value) {
[currentSummand addObject:value];
NSDecimalNumber *value = [NSDecimalNumber zero]; } else {
for (MPParsedProduct *product in products) { return nil;
NSDecimalNumber *productValue = [product evaluateWithError:error]; }
if (!productValue) { } else if (!value) {
return nil;
} else if (isAtBeginning || [[MPMathRules sharedRules] allowsImplicitMultiplication]) {
[currentSummand addObject:value];
} else {
self.error = MPParseError(NSMakeRange(valueRange.location, 0), @"Implicit Multiplication not allowed here");
return nil;
}
}
isAtBeginning = NO;
}
[summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
return [[MPTerm alloc] initWithSummands:summands.copy];
}
- (MPTerm *)nextValue:(NSRangePointer)range
{
MPToken *token = [tokenStream nextToken];
if (range) {
*range = token.range;
}
switch (token.tokenType) {
case MPNumberToken:
{
return [[MPTerm alloc] initWithNumber:token.number];
}
case MPVariableToken:
{
if(![[MPEvaluationContext sharedContext] isVariableDefined:token.variable]) {
self.error = MPParseError(token.range, @"Undefined Variable");
return nil;
}
return [[MPTerm alloc] initWithVariable:token.variable];
}
case MPGenericFunctionToken:
{
return [((MPFunction *)token) parseWithError:_error];
}
case MPSinToken:
{
NSRange sinTermRange;
MPTerm *sinTerm = [self nextValue:&sinTermRange];
if (!sinTerm) {
if (range) {
*range = NSUnionRange(token.range, sinTermRange);
}
return nil;
}
return [[MPTerm alloc] initWithSinOfTerm:sinTerm];
}
case MPCosToken:
{
NSRange cosTermRange;
MPTerm *cosTerm = [self nextValue:&cosTermRange];
if (!cosTerm) {
if (range) {
*range = NSUnionRange(token.range, cosTermRange);
}
return nil;
}
return [[MPTerm alloc] initWithSinOfTerm:cosTerm];
}
case MPTanToken:
{
NSRange tanTermRange;
MPTerm *tanTerm = [self nextValue:&tanTermRange];
if (!tanTerm) {
if (range) {
*range = NSUnionRange(token.range, tanTermRange);
}
return nil;
}
return [[MPTerm alloc] initWithTanOfTerm:tanTerm];
}
case MPEOFToken:
{
self.error = MPParseError(token.range, @"Unexpected End. Expected Number.");
return nil;
}
default:
{
self.error = MPParseError(token.range, @"Unexpected Symbol. Expected Number.");
return nil; return nil;
} }
value = [value decimalNumberByAdding:productValue];
} }
return value;
} }
@end @end

View File

@@ -0,0 +1,16 @@
//
// MPExpressionLexer.h
// MathPad
//
// Created by Kim Wittenburg on 19.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPExpression.h"
@interface MPExpressionTokenizer : NSObject
- (NSArray *)tokenizeExpression:(MPExpression *)expression; // Returns MPToken's
@end

View File

@@ -0,0 +1,115 @@
//
// MPExpressionLexer.m
// MathPad
//
// Created by Kim Wittenburg on 19.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionTokenizer.h"
#import "MPToken.h"
#import "MPFunction+MPToken.h"
#import "NSRegularExpression+MPParsingAdditions.h"
#define MPRangeExists(range) (range.location != NSNotFound)
@implementation MPExpressionTokenizer
- (NSArray *)tokenizeExpression:(MPExpression *)expression
{
NSMutableArray *tokens = [[NSMutableArray alloc] init];
for (NSUInteger index = 0; index < expression.numberOfElements; index++) {
id <MPExpressionElement> element = [expression elementAtIndex:index];
if ([element isFunction]) {
[tokens addObject:element];
} else {
[tokens addObjectsFromArray:[self tokenizeElement:(NSString *)element]];
}
}
return tokens;
}
- (NSArray *)tokenizeElement:(NSString *)element
{
NSUInteger lexLocation = 0;
NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]];
NSString *regexStringFormat = @"\\A(?:"
@"(\\*)|"
@"([+-](?:\\s*[+-])*)|"
@"((?:\\d+(?:%@\\d+)?)|(?:\\s%@\\d+))|"
@"(sin)|"
@"(cos)|"
@"(tan)|"
@"([A-Za-z])|"
@"(=)|"
@"(\\s)"
@")";
NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString
options:0
error:NULL];
NSMutableArray *tokens = [[NSMutableArray alloc] init];
while (lexLocation < element.length) {
NSTextCheckingResult *match = [regex firstMatchInString:element fromIndex:lexLocation];
NSRange range = NSMakeRange(lexLocation, 1);
MPTokenType tokenType = MPUnidentifiedToken;
if (match) {
NSRange multiplicationSymbolRange = [match rangeAtIndex:1];
NSRange operatorRange = [match rangeAtIndex:2];
NSRange numberRange = [match rangeAtIndex:3];
NSRange sinRange = [match rangeAtIndex:4];
NSRange cosRange = [match rangeAtIndex:5];
NSRange tanRange = [match rangeAtIndex:6];
NSRange variableRange = [match rangeAtIndex:7];
NSRange equalsRange = [match rangeAtIndex:8];
NSRange whitespaceRange = [match rangeAtIndex:9];
if (MPRangeExists(multiplicationSymbolRange)) {
range = multiplicationSymbolRange;
tokenType = MPMultiplicationSymbolToken;
} else if (MPRangeExists(operatorRange)) {
range = operatorRange;
tokenType = MPOperatorListToken;
} else if (MPRangeExists(numberRange)) {
range = numberRange;
tokenType = MPNumberToken;
} else if (MPRangeExists(sinRange)) {
range = sinRange;
tokenType = MPSinToken;
} else if (MPRangeExists(cosRange)) {
range = cosRange;
tokenType = MPCosToken;
} else if (MPRangeExists(tanRange)) {
range = tanRange;
tokenType = MPTanToken;
} else if (MPRangeExists(variableRange)) {
range = variableRange;
tokenType = MPVariableToken;
} else if (MPRangeExists(equalsRange)) {
range = equalsRange;
tokenType = MPEqualsToken;
} else if (MPRangeExists(whitespaceRange)) {
range = whitespaceRange;
tokenType = MPWhitespaceToken;
} else {
// Should not get here
range = NSMakeRange(lexLocation, 1);
tokenType = MPUnidentifiedToken;
}
}
lexLocation = NSMaxRange(range);
[tokens addObject:[[MPToken alloc] initWithTokenType:tokenType
range:range
inString:element]];
}
return tokens;
}
@end

View File

@@ -0,0 +1,14 @@
//
// MPFunction+MPToken.h
// MathPad
//
// Created by Kim Wittenburg on 19.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <MathKit/MathKit.h>
#import "MPToken.h"
@interface MPFunction (MPToken) <MPToken>
@end

View File

@@ -0,0 +1,34 @@
//
// MPFunction+MPToken.m
// MathPad
//
// Created by Kim Wittenburg on 19.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunction+MPToken.h"
@implementation MPFunction (MPToken)
- (MPTokenType)tokenType
{
return MPGenericFunctionToken;
}
- (NSRange)range
{
NSUInteger selfIndex = [self.parent indexOfElement:self];
return NSMakeRange([self.parent locationOfElementAtIndex:selfIndex], 1);
}
- (BOOL)exists
{
return YES;
}
- (NSString *)stringValue
{
return [self description];
}
@end

View File

@@ -9,11 +9,14 @@
@import Foundation; @import Foundation;
#import "MPExpressionElement.h" #import "MPExpressionElement.h"
#import "MPParseError.h" #import "MPParseError.h"
#import "MPTerm.h"
@class MPFunction, MPExpression, MPRangePath; @class MPFunction, MPExpression, MPRangePath;
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement> @interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
+ (NSString *)localizedFunctionName; // Override
#pragma mark Creation Methods #pragma mark Creation Methods
- (instancetype)init; - (instancetype)init;
@@ -38,7 +41,8 @@
- (id)elementAtIndexPath:(NSIndexPath *)indexPath; - (id)elementAtIndexPath:(NSIndexPath *)indexPath;
#pragma mark Evaluating Functions #pragma mark Evaluating Functions
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error; // Override
#pragma mark Messages #pragma mark Messages
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
@@ -52,4 +56,6 @@
- (NSString *)description; // Should be overridden - (NSString *)description; // Should be overridden
- (NSUInteger)hash;// Override - (NSUInteger)hash;// Override
- (id)copyWithZone:(NSZone *)zone; // Override
@end @end

View File

@@ -14,6 +14,11 @@
@implementation MPFunction @implementation MPFunction
+ (NSString *)localizedFunctionName
{
return NSLocalizedString(@"Function", @"Name of Generic Function.");
}
#pragma mark Creation Methods #pragma mark Creation Methods
- (instancetype)init - (instancetype)init
{ {
@@ -82,9 +87,9 @@
} }
#pragma mark Evaluating Functions #pragma mark Evaluating Functions
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error - (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error
{ {
return [NSDecimalNumber zero]; return nil;
} }
#pragma mark Notifications #pragma mark Notifications

View File

@@ -6,8 +6,12 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
// TODO: Rename to MPSyntaxError
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class MPRangePath;
#define MPParseError(range,key) [[MPParseError alloc] initWithErrorRange:range errorMessageKey:key] #define MPParseError(range,key) [[MPParseError alloc] initWithErrorRange:range errorMessageKey:key]
@interface MPParseError : NSObject @interface MPParseError : NSObject
@@ -23,4 +27,6 @@
@property (nonatomic) NSRange errorRange; @property (nonatomic) NSRange errorRange;
@property (nonatomic, copy) NSString *localizedErrorMessage; @property (nonatomic, copy) NSString *localizedErrorMessage;
- (MPRangePath *)rangePath;
@end @end

View File

@@ -8,6 +8,9 @@
#import "MPParseError.h" #import "MPParseError.h"
#import "MPRangePath.h"
#import "NSIndexPath+MPAdditions.h"
@implementation MPParseError @implementation MPParseError
- (instancetype)initWithErrorRange:(NSRange)errorRange errorMessageKey:(NSString *)key - (instancetype)initWithErrorRange:(NSRange)errorRange errorMessageKey:(NSString *)key
@@ -30,6 +33,14 @@
return self; return self;
} }
- (MPRangePath *)rangePath
{
NSIndexPath *location = self.pathToExpression;
location = [location indexPathByPreceedingIndex:self.errorRange.location];
MPRangePath *rangePath = [MPRangePath rangePathWithLocation:location length:self.errorRange.length];
return rangePath;
}
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"MPParseError<at=(%ld, %ld) message=\"%@\">", self.errorRange.location, self.errorRange.length, self.localizedErrorMessage]; return [NSString stringWithFormat:@"MPParseError<at=(%ld, %ld) message=\"%@\">", self.errorRange.location, self.errorRange.length, self.localizedErrorMessage];

26
MathPad/MPTerm.h Normal file
View File

@@ -0,0 +1,26 @@
//
// MPTerm.h
// MathPad
//
// Created by Kim Wittenburg on 27.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@import Foundation;
@interface MPTerm : NSObject
- (instancetype)initWithBlock:(NSDecimalNumber *(^)())block; // Designated Initializer
- (instancetype)initWithNumber:(NSDecimalNumber *)number;
- (instancetype)initWithSummands:(NSArray *)summands; // array of MPTerms
- (instancetype)initWithFactors:(NSArray *)factors; // array of MPTerms
- (instancetype)initWithVariable:(NSString *)variable;
- (instancetype)initWithSinOfTerm:(MPTerm *)term;
- (instancetype)initWithCosOfTerm:(MPTerm *)term;
- (instancetype)initWithTanOfTerm:(MPTerm *)term;
- (NSDecimalNumber *)evaluate;
@end

94
MathPad/MPTerm.m Normal file
View File

@@ -0,0 +1,94 @@
//
// MPTerm.m
// MathPad
//
// Created by Kim Wittenburg on 27.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPTerm.h"
#import "MPEvaluationContext.h"
@interface MPTerm ()
@property (nonatomic, copy) NSDecimalNumber *(^block)();
@end
@implementation MPTerm
- (instancetype)initWithBlock:(NSDecimalNumber *(^)())block
{
self = [super init];
if (self) {
self.block = block;
}
return self;
}
- (instancetype)initWithNumber:(NSDecimalNumber *)number
{
return [self initWithBlock:^NSDecimalNumber *{
return number;
}];
}
- (instancetype)initWithSummands:(NSArray *)summands
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *value = [NSDecimalNumber zero];
for (MPTerm *term in summands) {
value = [value decimalNumberByAdding:[term evaluate]];
}
return value;
}];
}
- (instancetype)initWithFactors:(NSArray *)factors
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *value = [NSDecimalNumber one];
for (MPTerm *term in factors) {
value = [value decimalNumberByMultiplyingBy:[term evaluate]];
}
return value;
}];
}
- (instancetype)initWithVariable:(NSString *)variable
{
return [self initWithBlock:^NSDecimalNumber *{
return [[MPEvaluationContext sharedContext] valueForVariable:variable];
}];
}
- (instancetype)initWithSinOfTerm:(MPTerm *)term
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *sinValue = [term evaluate];
return [[NSDecimalNumber alloc] initWithDouble:sin(sinValue.doubleValue)];
}];
}
- (instancetype)initWithCosOfTerm:(MPTerm *)term
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *cosValue = [term evaluate];
return [[NSDecimalNumber alloc] initWithDouble:cos(cosValue.doubleValue)];
}];
}
- (instancetype)initWithTanOfTerm:(MPTerm *)term
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *tanValue = [term evaluate];
return [[NSDecimalNumber alloc] initWithDouble:tan(tanValue.doubleValue)];
}];
}
- (NSDecimalNumber *)evaluate
{
return self.block();
}
@end

60
MathPad/MPToken.h Normal file
View File

@@ -0,0 +1,60 @@
//
// MPToken.h
// MathPad
//
// Created by Kim Wittenburg on 19.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, MPTokenType) {
MPEOFToken = 0,
MPMultiplicationSymbolToken,
MPOperatorListToken,
MPSinToken,
MPCosToken,
MPTanToken,
MPNumberToken,
MPVariableToken,
MPEqualsToken,
MPGenericFunctionToken,
MPWhitespaceToken,
MPUnidentifiedToken,
MPCompoundToken
};
@protocol MPToken <NSObject>
- (MPTokenType)tokenType;
- (NSRange)range;
- (NSString *)stringValue;
@end
@interface MPToken : NSObject <MPToken>
- (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation;
- (instancetype)initWithRange:(NSRange)range inString:(NSString *)input;
- (instancetype)initWithTokenType:(MPTokenType)tokenType
range:(NSRange)range
inString:(NSString *)input;
@end
@interface MPToken (MPTokenExtension)
// Methods are only available for respective token type
- (NSUInteger)numberOfOperators;
- (NSDecimalNumber *)operatorValue;
- (NSDecimalNumber *)number;
- (NSString *)variable;
@end

104
MathPad/MPToken.m Normal file
View File

@@ -0,0 +1,104 @@
//
// MPToken.m
// MathPad
//
// Created by Kim Wittenburg on 19.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPToken.h"
@implementation MPToken {
NSRange _range;
MPTokenType _tokenType;
NSString *_stringValue;
}
- (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation
{
self = [super init];
if (self) {
_range = NSMakeRange(eofLocation, 0);
}
return self;
}
- (instancetype)initWithRange:(NSRange)range inString:(NSString *)input
{
self = [super init];
if (self) {
_range = range;
_stringValue = [input substringWithRange:range].copy;
}
return self;
}
- (instancetype)initWithTokenType:(MPTokenType)tokenType
range:(NSRange)range
inString:(NSString *)input
{
self = [self initWithRange:range
inString:input];
if (self) {
_tokenType = tokenType;
}
return self;
}
- (NSRange)range
{
return _range;
}
- (MPTokenType)tokenType
{
return _tokenType;
}
- (NSString *)stringValue
{
return _stringValue;
}
- (NSString *)description
{
return self.stringValue;
}
@end
@implementation MPToken (MPTokenExtension)
- (NSUInteger)numberOfOperators
{
NSString *operatorString = [[self.stringValue componentsSeparatedByString:@" "] componentsJoinedByString:@""];
return operatorString.length;
}
- (NSDecimalNumber *)operatorValue
{
NSString *operatorString = [[self.stringValue componentsSeparatedByString:@" "] componentsJoinedByString:@""];
NSDecimalNumber *value = [NSDecimalNumber one];
for (NSUInteger charIndex = 0; charIndex < operatorString.length; charIndex++) {
NSString *operator = [operatorString substringWithRange:NSMakeRange(charIndex, 1)];
if ([operator isEqualToString:@"-"]) {
value = [value decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:-1]];
}
}
return value;
}
- (NSDecimalNumber *)number
{
return [NSDecimalNumber decimalNumberWithString:self.stringValue
locale:[NSLocale currentLocale]];
}
- (NSString *)variable
{
return self.stringValue;
}
@end

27
MathPad/MPTokenStream.h Normal file
View File

@@ -0,0 +1,27 @@
//
// MPTokenStream.h
// MathPad
//
// Created by Kim Wittenburg on 20.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPToken.h"
@interface MPTokenStream : NSObject
- (instancetype)initWithTokens:(NSArray *)tokens;
@property (nonatomic, copy) NSArray *tokens;
@property (nonatomic, getter=isIgnoringWhitespaceTokens) BOOL ignoringWhitespaceTokens; // Default: YES
@property (readonly, nonatomic) NSUInteger currentLocation;
- (void)reset;
- (BOOL)hasMoreTokens;
- (MPToken *)nextToken;
- (MPToken *)nextTokenOfType:(MPTokenType)type;
@end

90
MathPad/MPTokenStream.m Normal file
View File

@@ -0,0 +1,90 @@
//
// MPTokenStream.m
// MathPad
//
// Created by Kim Wittenburg on 20.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPTokenStream.h"
@interface MPTokenStream ()
@property (readwrite, nonatomic) NSUInteger currentLocation;
@end
@implementation MPTokenStream {
NSUInteger currentTokenIndex;
NSUInteger eofLocation;
}
- (instancetype)initWithTokens:(NSArray *)tokens
{
self = [super init];
if (self) {
self.tokens = tokens;
self.ignoringWhitespaceTokens = YES;
if (tokens.count > 0) {
eofLocation = NSMaxRange([tokens.lastObject range]);
}
[self reset];
}
return self;
}
- (void)skipWhitespaces
{
if (!self.isIgnoringWhitespaceTokens) {
return;
}
while (currentTokenIndex < self.tokens.count) {
MPToken *token = self.tokens[currentTokenIndex];
if (token.tokenType != MPWhitespaceToken) {
return;
}
self.currentLocation = NSMaxRange(token.range);
++currentTokenIndex;
}
}
- (void)reset
{
currentTokenIndex = 0;
self.currentLocation = 0;
[self skipWhitespaces];
}
- (BOOL)hasMoreTokens
{
[self skipWhitespaces];
return currentTokenIndex < self.tokens.count;
}
- (MPToken *)nextToken
{
[self skipWhitespaces];
if (currentTokenIndex >= self.tokens.count) {
return [[MPToken alloc] initEOFTokenAtLocation:eofLocation];
} else {
MPToken *token = self.tokens[currentTokenIndex++];
self.currentLocation = NSMaxRange(token.range);
return token;
}
}
- (MPToken *)nextTokenOfType:(MPTokenType)type
{
[self skipWhitespaces];
if (currentTokenIndex >= self.tokens.count) {
return nil;
} else {
MPToken *token = self.tokens[currentTokenIndex];
if (token.tokenType != type) {
return nil;
}
++currentTokenIndex;
self.currentLocation = NSMaxRange(token.range);
return token;
}
}
@end