diff --git a/MathPad/MPException.m b/MathPad/MPException.m index cf228b8..8ce8ea9 100644 --- a/MathPad/MPException.m +++ b/MathPad/MPException.m @@ -7,8 +7,6 @@ // #import "MPException.h" -#import "MPException_Private.h" NSString *MPIllegalSymbolException = @"MPIllegalSymbolException"; -NSString *MPIllegalSymbolExceptionReason = @"Only NSString and MPFunction objects are valid symbols."; NSString *MPIllegalSymbolExceptionSymbolKey = @"MPIllegalSymbolExceptionSymbolKey"; \ No newline at end of file diff --git a/MathPad/MPExpression.h b/MathPad/MPExpression.h index f5c5ee7..7eb781d 100644 --- a/MathPad/MPExpression.h +++ b/MathPad/MPExpression.h @@ -8,6 +8,11 @@ @class MPExpression, MPFunction; +extern NSString *MPAdditionOperator; +extern NSString *MPSubtractionOperator; +extern NSString *MPMultiplicationOperator; +extern NSString *MPDivisionOperator; + @interface MPExpression : NSObject #pragma mark Creation Methods @@ -25,6 +30,8 @@ @interface MPExpression (MPExpressionExtensionMethods) ++ (NSArray *)operators; + #pragma mark Creation Methods - (id)init; @@ -47,12 +54,6 @@ - (BOOL)isEqualToExpression:(MPExpression *)anExpression; -// TODO: prefix and suffix operator -/* -- (BOOL)hasPrefix:(NSString *)aString; -- (BOOL)hasSuffix:(NSString *)aString; -*/ - - (MPExpression *)expressionByAppendingString:(NSString *)aString; - (MPExpression *)expressionByAppendingFunction:(MPFunction *)aFunction; - (MPExpression *)expressionByAppendingExpression:(MPExpression *)anExpression; diff --git a/MathPad/MPExpression.m b/MathPad/MPExpression.m index a4befe4..73d604e 100644 --- a/MathPad/MPExpression.m +++ b/MathPad/MPExpression.m @@ -9,7 +9,11 @@ #import "MPExpression.h" #import "MPFunction.h" #import "MPException.h" -#import "MPException_Private.h" + +NSString *MPAdditionOperator = @"+"; +NSString *MPSubtractionOperator = @"-"; +NSString *MPMultiplicationOperator = @"*"; +NSString *MPDivisionOperator = @"/"; @interface MPExpression (MPExpressionPrivate) @@ -71,7 +75,6 @@ - (id)mutableCopyWithZone:(NSZone *)zone { - // TODO: Test Copying MPMutableExpression *mutableCopy = [[MPMutableExpression allocWithZone:zone] initWithSymbols:_symbols]; return mutableCopy; } @@ -108,12 +111,13 @@ if (!([symbol isKindOfClass:[NSString class]] || [symbol isKindOfClass:[MPFunction class]])) { @throw [NSException exceptionWithName:MPIllegalSymbolException - reason:MPIllegalSymbolExceptionReason + reason:@"Only NSString and MPFunction objects are valid symbols." userInfo:@{MPIllegalSymbolExceptionSymbolKey: symbol}]; } } } +// TODO: Deal with empty strings - (NSArray *)repairedSymbols:(NSArray *)symbols { NSMutableArray *mutableSymbols = [symbols mutableCopy]; @@ -127,7 +131,7 @@ id last = symbols[index-1]; id current = symbols[index]; if ([last isKindOfClass:[NSString class]] && [current isKindOfClass:[NSString class]]) { - NSMutableString *new = [NSMutableString stringWithFormat:@"%@%@", last, current]; + NSString *new = [NSString stringWithFormat:@"%@%@", last, current]; [symbols replaceObjectAtIndex:index-1 withObject:new]; [symbols removeObjectAtIndex:index]; index--; @@ -156,6 +160,11 @@ @implementation MPExpression (MPExpressionExtensionMethods) ++ (NSArray *)operators +{ + return @[MPAdditionOperator, MPSubtractionOperator, MPMultiplicationOperator, MPDivisionOperator]; +} + #pragma mark Creation Methods - (instancetype)init @@ -279,8 +288,6 @@ return [_symbols isEqualToArray:anExpression->_symbols]; } -// TODO: prefix and suffix operators - - (MPExpression *)expressionByAppendingString:(NSString *)aString { return [self expressionByAppendingSymbols:@[aString]]; @@ -333,11 +340,34 @@ - (NSString *)description { -#warning Unimplemented Method - /* - Join the symbols regarding pre- and suffix operators. If there is no operator a * should be inserted - */ - return [super description]; + NSMutableString *description = [[NSMutableString alloc] init]; + NSUInteger index = 0; + for (id symbol in _symbols) { + if ([symbol isKindOfClass:[NSString class]]) { + NSMutableString *correctedSymbol = [[symbol stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy]; + // Prefix operator + if (symbol != _symbols[0]) { + unichar prefix = [correctedSymbol characterAtIndex:0]; + if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) { + [correctedSymbol insertString:@"*" atIndex:0]; + } + } + // Suffix operator + if (symbol != [_symbols lastObject]) { + unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1]; + if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) { + [correctedSymbol appendString:@"*"]; + } + } + [description appendString:correctedSymbol]; + } else if (index > 0 && [_symbols[index-1] isKindOfClass:[MPFunction class]]) { + [description appendFormat:@"*%@", [symbol description]]; + } else { + [description appendString:[symbol description]]; + } + index++; + } + return description; } - (NSUInteger)hash @@ -347,40 +377,6 @@ @end -@interface MPMutableExpression (MPMutableExpressionPrivate) - -- (NSArray *)makeSymbols:(NSArray *)symbols mutable:(BOOL)mutable copySymbols:(BOOL)copy; - -@end - -@implementation MPMutableExpression (MPMutableExpressionPrivate) - -- (NSArray *)makeSymbols:(NSArray *)symbols mutable:(BOOL)mutable copySymbols:(BOOL)copy -{ - NSMutableArray *newSymbols = [[NSMutableArray alloc] initWithCapacity:symbols.count]; - for (id symbol in symbols) { - id newSymbol; - if ([symbol isKindOfClass:[NSString class]]) { - if (mutable) { - newSymbol = [symbol mutableCopy]; - } else { - newSymbol = [symbol copy]; - } - } else { - if (copy) { - newSymbol = [symbol copy]; - } else { - newSymbol = symbol; - } - } - [newSymbols addObject:newSymbol]; - } - return newSymbols; -} - -@end - -// A mutable expression uses NSMutableString objects instead of NSString objects @implementation MPMutableExpression - (instancetype)initWithSymbols:(NSArray *)symbols @@ -389,25 +385,15 @@ self = [super initWithSymbols:nil]; if (self) { symbols = [self repairedSymbols:symbols]; - _symbols = [self makeSymbols:symbols - mutable:YES - copySymbols:YES]; + _symbols = [[NSMutableArray alloc] initWithArray:symbols copyItems:YES]; } return self; } -- (id)symbolAtIndex:(NSUInteger)index -{ - id symbol = _symbols[index]; - if ([symbol isKindOfClass:[NSString class]]) { - return [symbol copy]; - } - return symbol; -} - - (NSArray *)symbols { - return [self makeSymbols:_symbols mutable:NO copySymbols:NO]; + // Return an immutable array: + return [_symbols copy]; } - (void)replaceSymbolsInRange:(NSRange)range withSymbols:(NSArray *)symbols @@ -434,7 +420,7 @@ // Perform the insertion if (symbols.count > 0) { - NSArray *newSymbols = [self makeSymbols:symbols mutable:YES copySymbols:YES]; + NSArray *newSymbols = [[NSArray alloc] initWithArray:symbols copyItems:YES]; [(NSMutableArray *)_symbols replaceObjectsInRange:NSMakeRange(startIndex, 0) withObjectsFromArray:newSymbols]; } @@ -455,8 +441,8 @@ splitSymbolIndex++; } if (splitOffset != 0) { - NSMutableString *leftPart = [[splitSymbol substringToIndex:splitOffset] mutableCopy]; - NSMutableString *rightPart = [[splitSymbol substringFromIndex:splitOffset] mutableCopy]; + NSString *leftPart = [splitSymbol substringToIndex:splitOffset]; + NSString *rightPart = [splitSymbol substringFromIndex:splitOffset]; [(NSMutableArray *)_symbols replaceObjectAtIndex:splitSymbolIndex withObject:leftPart]; splitSymbolIndex++; [(NSMutableArray *)_symbols insertObject:rightPart atIndex:splitSymbolIndex]; diff --git a/MathPadTests/MPExpressionTests.m b/MathPadTests/MPExpressionTests.m index 0f22bd1..c5d5baf 100644 --- a/MathPadTests/MPExpressionTests.m +++ b/MathPadTests/MPExpressionTests.m @@ -154,7 +154,62 @@ XCTAssertEqualObjects(expression2, expression3); } -// TODO: Test prefix- and suffix operators via -description method +- (void)testDescription { + // Test Simple Expressions + MPExpression *testExpression = [[MPExpression alloc] initWithString:@"1234"]; + XCTAssertEqualObjects([testExpression description], @"1234"); + + testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]]; + XCTAssertEqualObjects([testExpression description], @"[]"); + + // Test function after literal without explicit operator + testExpression = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]]; + XCTAssertEqualObjects([testExpression description], @"123*[]"); + + // Test function after literal with explicit operator + testExpression = [[MPExpression alloc] initWithSymbols:@[@"123+", [[MPFunction alloc] init]]]; + XCTAssertEqualObjects([testExpression description], @"123+[]"); + + // Test literal after function without explicit operator + testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]]; + XCTAssertEqualObjects([testExpression description], @"[]*123"); + + // Test literal after function with explicit operator + testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"-123"]]; + XCTAssertEqualObjects([testExpression description], @"[]-123"); + + // Test function after function without explicit operator + testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], [[MPFunction alloc] init]]]; + XCTAssertEqualObjects([testExpression description], @"[]*[]"); + + // Test function after function with explicit operator + testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"-", [[MPFunction alloc] init]]]; + XCTAssertEqualObjects([testExpression description], @"[]-[]"); + + + // Test whitespaces in literal + testExpression = [[MPExpression alloc] initWithSymbols:@[@" 123 + ", [[MPFunction alloc] init]]]; + XCTAssertEqualObjects([testExpression description], @"123 +[]"); +} + +- (void)testCopying { + MPExpression *baseExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]]; + MPExpression *copy = [baseExpression copy]; + + XCTAssertNotEqual(copy, baseExpression); + XCTAssertNotEqual([baseExpression symbolAtIndex:0], [copy symbolAtIndex:0]); +} + +- (void)testMutableCopying { + MPExpression *baseExpression = [[MPExpression alloc] initWithString:@"123"]; + MPMutableExpression *expression1 = [baseExpression mutableCopy]; + [expression1 appendFunction:[[MPFunction alloc] init]]; + MPExpression *expression2 = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]]; + + XCTAssertEqualObjects(expression1, expression2); + XCTAssertNotEqualObjects(baseExpression, expression1); +} + // TODO: Test evaluating expressions @end