From af8f26dee937212cf649b0706ccd6d7c17c67139 Mon Sep 17 00:00:00 2001 From: Kim Wittenburg Date: Tue, 16 Sep 2014 20:07:57 +0200 Subject: [PATCH] Improved Documentation and Code Style --- MathPad.xcodeproj/project.pbxproj | 2 + MathPad/MPExpression.h | 524 ++++++++++++++++++------------ MathPad/MPExpression.m | 316 ++++++++++++------ MathPad/MPFunction.m | 2 +- 4 files changed, 532 insertions(+), 312 deletions(-) diff --git a/MathPad.xcodeproj/project.pbxproj b/MathPad.xcodeproj/project.pbxproj index 2928bf0..93c8c55 100644 --- a/MathPad.xcodeproj/project.pbxproj +++ b/MathPad.xcodeproj/project.pbxproj @@ -948,6 +948,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; @@ -986,6 +987,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; diff --git a/MathPad/MPExpression.h b/MathPad/MPExpression.h index 83fa305..556c8cf 100644 --- a/MathPad/MPExpression.h +++ b/MathPad/MPExpression.h @@ -14,19 +14,41 @@ /*! @class MPExpression - @brief An expression is the base object for any mathematical expression. + @brief An @c MPExpression object is the base object for any mathematical + expression. @discussion Every expression consists of string elements (represented by the - @c NSString class) and function (represented by the @c MPFunction - class) elements which both can be contained within an expression. - Functions in turn can have expressions as elements (also called - 'children' in this context). Both expressions and functions are - mutable. + @c NSString class) and functions (represented by the @c + MPFunction class) elements which both can be contained within an + expression. Functions in turn can have expressions as elements + (also called 'children' in this context). Both expressions and + functions are mutable. Through this organization expression are organized in a tree-like structure (called the 'expression tree') allowing easy and logical access to each element. + To query the contents of an expressions there are two options: + You can query on a per-element basis (that is string elements and + function elements as described before). This is called the @em + index reference frame (or @em indexed reference frame). You also + can query an expression on a per-symbol basis. This is called the + @em location reference frame (or @c located reference frame). + Using the located reference frame is useful if you are dealing + with user actions since the user does not necessarily see a + visual separation between different elements but only sees the + symbols that are drawn on the screen. You can convert between the + two reference frames using the following methods: + @code +- (NSUInteger)indexOfElementAtSymbolLocation:offset: // Converts from the located to the indexed reference frame +- (NSUInteger)locationOfElementAtIndex: // Converts from the indexed to the located reference frame + @endcode + + In the indexed reference frame both functions and strings have a + dimension of 1. In the located reference frame the length of a + string is determined by sending it a @c -length message. In that + frame functions still have a length of 1. + An expression can evaluate itself giving you either a result or possibly an error if the expression was not constructed correctly. @@ -41,8 +63,8 @@ @method init @brief Initlializes a newly created expression. - @discussion This method is a convenience initializer to initialize an empty - expression. + @discussion This method is a convenience initializer to initialize an + expression with @c 0 elements. @return An expression. */ @@ -51,7 +73,7 @@ /*! @method initWithElement: - @brief Initializes a newly created expression with one element. + @brief Initializes a newly created expression with @c element. @discussion This method is a convenience initializer to initialize an expression with a single element. @@ -69,19 +91,25 @@ @method initWithElements: @brief Initializes a newly created expression with the given elements. - @discussion This method is the designated initializer for the @c MPExpression + @discussion All elements must conform to the @c MPExpressionElement protocol. + If one or more objects do not conform to that protocol an @c + MPIllegalElementException is raised. + + This method is the designated initializer for the @c MPExpression class. @param elements - The elements that should be added to the expression. Each element - is copied and the copy is then added to the expression. + The elements that should be added to the expression. Every + element must conform to the @c MPExpressionElement protocol. Each + element is copied and the copy is then added to the expression. + The object in the @c elements array is not modified. @return An expression containing the elements from @c elements. */ - (instancetype)initWithElements:(NSArray *)elements; /* designated initializer */ -#pragma mark Working With the Expression Tree +#pragma mark Querying Expressions /*! @@ -90,8 +118,8 @@ @discussion Expressions are organized in a tree-like structure. Through this property an expression's containing function can be accessed. - You should not set this property manually because that can cause - inconsistencies in the expression tree. + @warning You should not set this property manually unless you are + implementing a subclass of @c MPFunction. @return The parent of the receiver or @c nil if the receiver is the root expression. @@ -99,64 +127,21 @@ @property (nonatomic, weak) MPFunction *parent; -/*! - @method fixElements - @brief Repairs any inconsistencies in the receiver. - - @discussion This method goes over all elements in the receiver and tries to - repair inconsistencies that occured when mutating the receiver. - - Since this method is called automatically everytime the receiver - is mutated there should be little need for you to call it - yourself. - */ -- (void)fixElements; - - -#pragma mark Primitive Methods - - -/*! - @method length - @brief Returns the length of the receiver. - - @discussion The length of an expression is calculated by walking over each - element in the receiver and sending it a @c length message. This - method should be used to determine the number of digits or - symbols in an expression. - - To address a symbol in the expression counted in the @c length - reference frame of an expression the word 'location' is used. - - @return The length of the receiver. This is the number of symbols in all - elements in the receiver where a function element is counted as a - single symbol. - */ -- (NSUInteger)length; - - /*! @method numberOfElements @brief Returns the number of elements in the receiver. @discussion The number of elements may vary from the number of elements that were added to the receiver (using either @c -initWithElements: or - @ -replaceElementsInRange:withElements:) - - To address a specific symbol in an expression the word 'index' is - used. The index of elements is used when you access an - expression's elements using subscript syntax. + one of the several mutation methods). The number of elements is + expressed in the indexed reference frame. (The respective method + for the located reference frame is @c -length). @return The current number of elements in the receiver. */ - (NSUInteger)numberOfElements; -/* Subscripting is supported for indexes and elements */ -- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx; -- (id)objectAtIndexedSubscript:(NSUInteger)idx; - - /*! @method elementAtIndex: @brief Returns the element at @c anIndex. @@ -165,23 +150,24 @@ if you mutate a @c MPFunction object returned from this function the changes will be reflected in the receiver. - This method can also be called using indexed subscript getter - syntax. + @note This method can also be called using indexed subscript + getter syntax. @param anIndex - The index of the element. If the index is greater than or equal - to the number of elements in the receiver an @c NSRangeException - is raised. + The index of the element expressed in the indexed reference + frame. If the index is greater than or equal to the number of + elements in the receiver an @c NSRangeException is raised. - @return The element located at @c anIndex. + @return The element at @c anIndex. */ - (id)elementAtIndex:(NSUInteger)anIndex; /*! - @method elementsInRange: + @method elementsInIndexedRange: @brief Returns an array of the elements that are located in the - specified range. The range is specified in indexes. + specified range. The range is specified in the indexed reference + frame. @discussion The objects in the returned array are not copied before they are returned. You should be aware of the fact that mutations to any @@ -191,103 +177,15 @@ NSRangeException is raised. @param range - The requested range within the receiver's bounds. + The requested range within the receiver's bounds expressed in the + indexed reference frame. @return An array of objects that conform to the @c MPExpressionElement protocol (that is @c NSString objects and @c MPFunction objects). The length of the returned array is equal to the length of the specified range. */ -- (NSArray *)elementsInRange:(NSRange)range; - - -/*! - @method indexOfElement: - @brief Returns the index of @c element or @c NSNotFound if it was not - found. - - @discussion - - @param - - @return - */ -#warning Implementation may be faulty -- (NSUInteger)indexOfElement:(id)element; - - -/*! - @method indexOfElementAtSymbolLocation:offset - @brief Calculates the index of the element the specified location points - to. - - @discussion The @c location is in the length reference frame whereas the - returned value is an element index. This method converts from - the former to the latter. - - If the location exceeds the receiver's bounds a @c - NSRangeException will be raised. - - @param location - The location of which you want the corresponding element index. - - @param offset - An output parameter that gets set to the offst into the symbol - whose index is returned. If location for example points to the - symbol @c '2' in the string element @c '123' the offset @c would - be set to @c 1. - - @return The index of the element the location points to. - */ -- (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: - @brief Replaces the elements in the given range with the contents of the - @c elements array. - - @discussion This is the most primitive mutation method of @c MPExpression. - Every other mutating method utlimately must call this method. - After the receiver has been mutated @c -fixElements is called to - restore integrity of the receiver. - - After the receiver has been mutated (and integrity has been - restored) the receiver sends a @c - -didChangeElementsInRangePath:replacementLength: to itself. For - more information see the documentation on that method. - - @param range - The @c range is specified in the length reference - frame. Because of this this method can be directly used for user - interaction. - - @param elements - The elements that should replace the symbols specified by @c - range. - */ -- (void)replaceSymbolsInRange:(NSRange)range - withElements:(NSArray *)elements; +- (NSArray *)elementsInIndexedRange:(NSRange)range; /*! @@ -302,6 +200,38 @@ - (NSArray *)elements; +#pragma mark Mutating Expressions + + +/*! + @method replaceSymbolsInRange:withElements: + @brief Replaces the elements in the given range with the contents of the + @c elements array. + + @discussion This is the most primitive mutation method of @c MPExpression. + Every other mutating method utlimately must call this method. + + + After the receiver has been mutated the integrety of the receiver + is restored. That basically means that subsequent strings are + joined and empty strings removed. After restoring integrity the + receiver sends a @c + -didChangeElementsInIndexedRangePath:replacementLength: to + itself. For more information see the documentation on that + method. + + @param range + The @c range of symbols (including functions) to replace + specified in the located reference frame. + + @param elements + The elements that should replace the symbols specified by @c + range. + */ +- (void)replaceSymbolsInRange:(NSRange)range + withElements:(NSArray *)elements; + + #pragma mark Evaluating Expressions @@ -313,7 +243,7 @@ want more control over the evaluation process use the @c evaluator property of the receiver. - @param errror + @param error 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 @@ -336,60 +266,71 @@ */ @property (readonly, nonatomic, strong) MPExpressionEvaluator *evaluator; + #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 + /*! - @method didChangeElementsInRangePath:replacementLength: + @method didChangeElementsInIndexedRangePath:replacementLength: @brief Called after the receiver has been mutated. @discussion This method does nothing more than notify it's parent that it has been mutated at the receiver's index. If you need to know about changes in an expression you should override this method instead - of @c -replaceSymbolsInRange:withElements because this method + of @c -replaceSymbolsInRange:withElements: because this method gives you information about the number of elements changed during the mutation. @param rangePath The range path at which the receiver was changed starting at the - receiver. + receiver. The range addressed by @c rangePath is expressed in the + indexed reference frame. @param replacementLength The number of elements replacing the elements specified by @c rangePath. */ -- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath - replacementLength:(NSUInteger)replacementLength; +- (void)didChangeElementsInIndexedRangePath:(MPRangePath *)rangePath + replacementLength:(NSUInteger)replacementLength; #pragma mark Basic NSObject Methods - - -/*! - @method isEqualToExpression: - @brief Returns wether the receiver is equal to @c anExpression. - - @param anExpression - The expression the receiver should be compared to. - - @return @c YES if @c anExpression is equal to the receiver, @c NO - otherwise. - */ +// TODO: Check this // - (BOOL)isEqualToExpression:(MPExpression *)anExpression; -- (NSString *)description; -- (NSUInteger)hash; - @end + /* --------------------------------------------------------------------------- */ /* Extension Methods */ /* --------------------------------------------------------------------------- */ + @interface MPExpression (MPExpressionExtension) -#pragma mark Working With the Expression Tree +#pragma mark Querying Expressions + + +/*! + @method length + @brief Returns the length of the receiver. + + @discussion The length of an expression is calculated by going over each + element in the receiver and sending it a @c -length message. This + method should be used to determine the number of digits or + symbols in an expression. + + The result of this method is expressed in the located reference + frame. The respective method for the indexed reference frame is + @c -numberOfSymbols. + + @return The length of the receiver. This is the number of symbols in all + elements in the receiver where a function element is counted as a + single symbol. + */ +- (NSUInteger)length; /*! @@ -397,27 +338,70 @@ @brief Returns the root expression from the receiver's expression tree. @discussion The root expression is the ultimate parent of all expressions and - functions in the expression tree. + functions in the expression tree. A root expression does not have + a parent. @return The root expression from the receiver's expression tree. */ - (MPExpression *)rootExpression; +/*! + @method indexPath + @brief Returns the index path of the receiver in the expression tree. + + @discussion The index path is calculated by going up the expression tree + collecting the respective index of the receiver. The indexes are + expressed in the indexed reference frame. If any of the indexes + exceed the respective receiver's bounds a @c NSRangeException is + raised. + + @return The index path of the receiver in the expression tree. + */ +- (NSIndexPath *)indexPath; + + +// Subscripting is supported in the indexed reference frame +- (id)objectAtIndexedSubscript:(NSUInteger)idx; + + +#pragma mark Working With Expressions + + +/*! + @method elementAtLocation: + @brief Returns the element that is located at @c location. + + @discussion This method finds an element in the located reference frame. If + @c location is greater or equal to the @c length of the receiver + a @c NSRangeException is raised. + + @param location + The location of the element to find expressed in the located + reference frame. + + @return The element located at @c location. + */ +- (id)elementAtLocation:(NSUInteger)location; + + /*! @method elementAtIndexPath: @brief Returns the element at the specified index path. - @discussion The returned object can be an @c NSString, a @c MPFunction or an - @c MPExpression depending on the element @c indexPath points to. - If any of the indexes exceed the bounds of the respective - receiver an @c NSRangeException is raised. + @discussion This method @em walks down the expression tree (including + functions) using the specified index path and finds the + corresponding element. The returned object can be an @c NSString, + a @c MPFunction or an @c MPExpression depending on the element @c + indexPath points to. If any of the indexes exceed the bounds of + the respective receiver an @c NSRangeException is raised. If the index path does not contain any indexes the receiver itself is returned. @param indexPath - The index path the required object is located at. + The index path the required object is located at. The indexes are + expressed in the indexed reference frame. @return The element located at @c indexPath. The element is not copied before it is returned. Be aware of the fact that any mutations @@ -427,46 +411,142 @@ /*! - @method elementsInRangePath: + @method elementsInIndexedRangePath: @brief Returns the elements in the specified range path. - @discussion If any of the indexes or the range exceed the bounds of the - respective receiver an @c NSRangeException is raised. + @discussion This method works similar to @c elementAtIndexPath: except that + it queries multiple elements at once. @param rangePath - The range path the requested objects are located at. + The range path the requested objects are located at. The complete + range path is expressed in the indexed reference frame. @return An array of objects specified by the range path. The returned elements are not copied before they are returned. Be aware that any mutations made to the returned objects are reflected in the receiver. */ -- (NSArray *)elementsInRangePath:(MPRangePath *)rangePath; +- (NSArray *)elementsInIndexedRangePath:(MPRangePath *)rangePath; + /*! - @method indexPath - @brief Returns the index path of the receiver in the expression tree. + @method indexOfElement: + @brief Returns the index of @c element or @c NSNotFound if it was not + found. - @discussion The index path is calculated by walking up the expression tree - collecting the respective index of the receiver. + @param element + The element to find. - @return The index path of the receiver in the expression tree. + @return The index of @c element expressed in the indexed reference frame. */ -- (NSIndexPath *)indexPath; +- (NSUInteger)indexOfElement:(id)element; -#pragma mark Working With Expressions +#pragma mark Converting Between Indexes and Locations + + +/*! + @method indexOfElementAtLocation:offset: + @brief Calculates the index of the element the specified location points + to. + + @discussion The @c location is in the located reference frame whereas the + returned value is an index. This method converts from the former + to the latter. + + This method prefers higher indexes. This means that if the + returned @c offset would be equal to the length of the element at + the calculated index, insead index+1 is returned and the @c + offset is set to @c 0. + + If the @c location exceeds the receiver's bounds a @c + NSRangeException will be raised. + + @param location + The location of which you want the corresponding element index. + + @param offset + An output parameter that gets set to the offst into the symbol + whose index is returned. If location for example points to the + symbol @c '2' in the string element @c '123' the offset @c would + be set to @c 1. + + @return The index of the element the location points to. + */ +- (NSUInteger)indexOfElementAtLocation:(NSUInteger)location offset:(out NSUInteger *)offset; + + +/*! + @method locationOfElementAtIndex: + @brief Calculates the location of the element at @c index. + + @discussion @c index is expressed in the indexed reference frame. Use this + method to convert an index into the located 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 indexedRangeForRange: + @brief Converts @c aRange from the located reference frame into the + indexed reference frame. + + @discussion If the range exceeds the receiver's bounds a @c NSRangeException + is raised. + + @param aRange + The range to be converted. Expressed in the located reference + frame. + + @return @c aRange converted into the indexed reference frame. + */ +- (NSRange)indexedRangeForRange:(NSRange)aRange; + + +/*! + @method rangeForIndexedRange: + @brief Converts @c aRange from the indexed reference frame into the + located reference frame. + + @discussion In the range exceeds the receiver's bounds a @c NSRangeException + is raised. + + @param aRange + The range to be converted. Expressed in the indexed reference + frame. + + @return @c aRange converted into the located reference frame. + */ +- (NSRange)rangeForIndexedRange:(NSRange)aRange; + + +#pragma mark Mutating Expressions + + +- (MPExpression *)subexpressionFromIndex:(NSUInteger)from; +- (MPExpression *)subexpressionToIndex:(NSUInteger)to; +- (MPExpression *)subexpressionWithIndexedRange:(NSRange)range; /*! @method subexpressionFromLocation: - @brief Creates a new expression from the specified index (inclusive) to - the end of the receiver. + @brief Creates a new expression from the specified location (inclusive) + to the end of the receiver. @discussion The elements in the newly created expression are copied to the - new expression. The location is specified in the length reference - frame. + new expression. The location is specified in the located + reference frame. If the given location exceeds the receiver's bounds a @c NSRangeException is raised. @@ -474,7 +554,8 @@ @param from The first location to be included in the new expression. - @return A new expression. + @return A new expression from the given location to the end of the + receiver. */ - (MPExpression *)subexpressionFromLocation:(NSUInteger)from; @@ -482,21 +563,20 @@ /*! @method subexpressionToLocation: @brief Creates a new expression from the beginning to the specified - index (exclusive). + location (exclusive). @discussion The elements in the newly created expression are copied to the - new expression. The location is specified in the length reference - frame. + new expression. The location is specified in the located + reference frame. If the given location exceeds the receiver's bounds a @c NSRangeException is raised. - @param to - The first location not to be included in the new expression or - the length of the new expression. + The first location not to be included in the new expression (or + the length of the new expression). - @return A new expression. + @return A new expression with the first @c to symbols of the receiver. */ - (MPExpression *)subexpressionToLocation:(NSUInteger)to; @@ -506,7 +586,7 @@ @brief Creates a new expression with the symbols in the specified range. @discussion The elements in the newly created expression are copied to the - new exoression. The range is specified in the length reference + new exoression. The range is specified in the located reference frame. If the given range exceeds the receiver's bounds a @c @@ -515,12 +595,13 @@ @param range The range from which to create the new expression. - @return A new expression. + @return A new expression with the symbols in the specified range. */ - (MPExpression *)subexpressionWithRange:(NSRange)range; -#pragma mark Mutating Expressions +- (void)replaceElementsInIndexedRange:(NSRange)range + withElements:(NSArray *)elements; /*! @@ -543,6 +624,14 @@ - (void)appendElements:(NSArray *)elements; +- (void)insertElement:(id)anElement + atIndex:(NSUInteger)index; + +- (void)insertElements:(NSArray *)elements + atIndex:(NSUInteger)index; + + + /*! @method insertElement:atLocation: @brief Inserts @c anElement at @c location. @@ -582,6 +671,8 @@ atLocation:(NSUInteger)location; +- (void)deleteElementsInIndexedRange:(NSRange)range; + /*! @method deleteElementsInRange: @brief Removes the elements specified by @c range from the receiver. @@ -611,5 +702,8 @@ */ // - (NSMutableArray *)mutableElements; +/* Subscripting is supported for elements in the indexed reference frame */ +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx; + @end diff --git a/MathPad/MPExpression.m b/MathPad/MPExpression.m index 5d9e8c6..5d3ca5d 100644 --- a/MathPad/MPExpression.m +++ b/MathPad/MPExpression.m @@ -6,8 +6,6 @@ // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // -// TODO: Reorganise header/implementation order. Drop the primitive/not primitive Approach - #import "MPExpression.h" #import "MPFunction.h" #import "MPRangePath.h" @@ -17,31 +15,32 @@ #import "MPExpressionEvaluator.h" + + @interface MPExpression () { NSMutableArray *__strong _elements; } + +- (void)fixElements; + @end + + @interface MPExpression (MPExpressionPrivate) +- (void)validateElements:(NSArray *)elements; + - (NSUInteger)lengthOfElements:(NSArray *)elements; -- (void)validateElements:(NSArray *)elements; - (BOOL)splitElementsAtLocation:(NSUInteger)location insertionIndex:(out NSUInteger *)insertionIndex; @end -@implementation MPExpression (MPExpressionPrivate) -- (NSUInteger)lengthOfElements:(NSArray *)elements -{ - NSUInteger length = 0; - for (id element in elements) { - length += element.length; - } - return length; -} + +@implementation MPExpression (MPExpressionPrivate) - (void)validateElements:(NSArray *)elements { @@ -54,6 +53,15 @@ } } +- (NSUInteger)lengthOfElements:(NSArray *)elements +{ + NSUInteger length = 0; + for (id element in elements) { + length += element.length; + } + return length; +} + - (BOOL)splitElementsAtLocation:(NSUInteger)location insertionIndex:(out NSUInteger *)insertionIndex { @@ -63,7 +71,7 @@ } NSUInteger splitOffset; - NSUInteger splitElementIndex = [self indexOfElementAtSymbolLocation:location + NSUInteger splitElementIndex = [self indexOfElementAtLocation:location offset:&splitOffset]; if (splitOffset != 0) { NSString *splitElement = (NSString *)self.elements[splitElementIndex]; @@ -79,6 +87,8 @@ @end + + @implementation MPExpression { NSUInteger _cachedLength; NSRange _editedRange; @@ -86,7 +96,10 @@ NSUInteger _replacementLength; } + #pragma mark Creation Methods + + - (instancetype)init { return [self initWithElements:@[]]; @@ -101,6 +114,7 @@ { self = [super init]; if (self) { + [self validateElements:elements]; _cachedLength = 0; _elements = [[NSMutableArray alloc] initWithArray:elements copyItems:YES]; @@ -109,7 +123,10 @@ return self; } -#pragma mark Working With the Expression Tree + +#pragma mark Private Methods + + - (void)fixElements { for (NSUInteger index = 0; index < self.elements.count; index++) { @@ -145,90 +162,33 @@ } } -#pragma mark Primitive Methods -- (NSUInteger)length -{ - if (_cachedLength == 0) { - _cachedLength = [self lengthOfElements:self.elements]; - } - return _cachedLength; -} + +#pragma mark Querying Expressions + - (NSUInteger)numberOfElements { return self.elements.count; } -- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx -{ - [self replaceSymbolsInRange:NSMakeRange(idx, 1) - withElements:@[obj]]; -} - -- (id)objectAtIndexedSubscript:(NSUInteger)idx -{ - return [self elementAtIndex:idx]; -} - - (id)elementAtIndex:(NSUInteger)anIndex { return self.elements[anIndex]; } -- (NSArray *)elementsInRange:(NSRange)range +- (NSArray *)elementsInIndexedRange:(NSRange)range { return [self.elements subarrayWithRange:range]; } -#warning If multiple equal expressions exist errors may occur... -- (NSUInteger)indexOfElement:(id)element +- (NSArray *)elements { - return [self.elements indexOfObject:element]; + return _elements; } -- (NSUInteger)indexOfElementAtSymbolLocation:(NSUInteger)location - offset:(out NSUInteger *)offset -{ - if (location == 0) { - if (offset != NULL) { - *offset = 0; - } - return 0; - } - - // Calculating elementIndex and splitOffset - NSUInteger totalLength = 0; - NSUInteger elementIndex = 0; - NSUInteger elementLength = 0; - for (id element in self.elements) { - elementLength = element.length; - totalLength += elementLength; - if (totalLength >= location) { - break; - } - ++elementIndex; - } - NSUInteger splitOffset = elementLength - (totalLength - location); - id element = self.elements[elementIndex]; - if (splitOffset == element.length) { - splitOffset = 0; - elementIndex++; - } - if (offset != NULL) { - *offset = splitOffset; - } - return elementIndex; -} +#pragma mark Mutating Expressions -- (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 withElements:(NSArray *)elements @@ -268,14 +228,13 @@ _replacementLength = elements.count + (didSplitStart ? 1 : 0) + (didSplitEnd ? 1 : 0); [self fixElements]; - [self didChangeElementsInRangePath:[[MPRangePath alloc] initWithRange:_editedRange] + [self didChangeElementsInIndexedRangePath:[[MPRangePath alloc] initWithRange:_editedRange] replacementLength:_replacementLength]; } -- (NSArray *)elements -{ - return _elements; -} + +#pragma mark Evaluating Expressions + - (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error { @@ -291,8 +250,11 @@ return _evaluator; } + #pragma mark Notifications -- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath + + +- (void)didChangeElementsInIndexedRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)replacementLength { NSUInteger selfIndex = [self.parent indexOfChild:self]; @@ -301,7 +263,10 @@ replacementLength:replacementLength]; } + #pragma mark Basic NSObject Methods + + /* - (BOOL)isEqual:(id)object { @@ -322,6 +287,7 @@ return [self.elements isEqualToArray:anExpression.elements]; } */ + - (NSString *)description { #warning Bad Implementation @@ -361,14 +327,20 @@ return [self.elements hash]; } + #pragma mark - NSCopying + + - (id)copyWithZone:(NSZone *)zone { MPExpression *copy = [[MPExpression allocWithZone:zone] initWithElements:self.elements]; return copy; } + #pragma mark - NSCoding + + - (id)initWithCoder:(NSCoder *)aDecoder { // TODO: Test Coding @@ -382,9 +354,22 @@ @end + + @implementation MPExpression (MPExpressionExtension) -#pragma mark Working With the Expression Tree + +#pragma mark Querying Expressions + + +- (NSUInteger)length +{ + if (_cachedLength == 0) { + _cachedLength = [self lengthOfElements:self.elements]; + } + return _cachedLength; +} + - (MPExpression *)rootExpression { if (self.parent == nil) { @@ -393,6 +378,31 @@ return [self.parent rootExpression]; } +- (NSIndexPath *)indexPath +{ + if (self.parent) { + NSUInteger selfIndex = [self.parent indexOfChild:self]; + return [[self.parent indexPath] indexPathByAddingIndex:selfIndex]; + } else { + return [[NSIndexPath alloc] init]; + } +} + +- (id)objectAtIndexedSubscript:(NSUInteger)idx +{ + return [self elementAtIndex:idx]; +} + + +#pragma mark Working With Expressions + + +- (id)elementAtLocation:(NSUInteger)location +{ + NSUInteger index = [self indexOfElementAtLocation:location offset:NULL]; + return [self elementAtIndex:index]; +} + - (id)elementAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.length == 0) { @@ -408,23 +418,106 @@ return nil; } -- (NSArray *)elementsInRangePath:(MPRangePath *)rangePath +- (NSArray *)elementsInIndexedRangePath:(MPRangePath *)rangePath { MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]]; - return [targetExpression elementsInRange:rangePath.rangeAtLastIndex]; + return [targetExpression elementsInIndexedRange:rangePath.rangeAtLastIndex]; } -- (NSIndexPath *)indexPath +#warning If multiple equal expressions exist errors may occur... +- (NSUInteger)indexOfElement:(id)element { - if (self.parent) { - NSUInteger selfIndex = [self.parent indexOfChild:self]; - return [[self.parent indexPath] indexPathByAddingIndex:selfIndex]; - } else { - return [[NSIndexPath alloc] init]; - } + return [self.elements indexOfObject:element]; +} + + +#pragma mark Converting Between Indexes and Locations + + +- (NSUInteger)indexOfElementAtLocation:(NSUInteger)location + offset:(out NSUInteger *)offset +{ + if (location == 0) { + if (offset != NULL) { + *offset = 0; + } + return 0; + } + + // Calculating elementIndex and splitOffset + NSUInteger totalLength = 0; + NSUInteger elementIndex = 0; + NSUInteger elementLength = 0; + for (id element in self.elements) { + elementLength = element.length; + totalLength += elementLength; + if (totalLength >= location) { + break; + } + ++elementIndex; + } + NSUInteger elementOffset = elementLength - (totalLength - location); + + id element = self.elements[elementIndex]; + if (elementOffset == element.length) { + elementOffset = 0; + elementIndex++; + } + if (offset != NULL) { + *offset = elementOffset; + } + return elementIndex; +} + +- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index +{ + NSUInteger location = 0; + for (NSUInteger i = 0; i < index; i++) { + location += [self elementAtIndex:i].length; + } + return location; +} + +- (NSRange)indexedRangeForRange:(NSRange)aRange +{ + NSUInteger startLocation = aRange.location; + NSUInteger endLocation = NSMaxRange(aRange); + NSUInteger startIndex = [self indexOfElementAtLocation:startLocation offset:NULL]; + NSUInteger endIndex = [self indexOfElementAtLocation:endLocation offset:NULL]; + return NSMakeRange(startIndex, endIndex-startIndex); +} + +- (NSRange)rangeForIndexedRange:(NSRange)aRange +{ + NSUInteger startIndex = aRange.location; + NSUInteger endIndex = NSMaxRange(aRange); + NSUInteger startLocation = [self locationOfElementAtIndex:startIndex]; + NSUInteger endLocation = [self locationOfElementAtIndex:endIndex]; + return NSMakeRange(startLocation, endLocation-startLocation); +} + + +#pragma mark Mutating Expressions + + +- (MPExpression *)subexpressionFromIndex:(NSUInteger)from +{ + NSUInteger fromLocation = [self locationOfElementAtIndex:from]; + return [self subexpressionFromLocation:fromLocation]; +} + +- (MPExpression *)subexpressionToIndex:(NSUInteger)to +{ + NSUInteger toLocation = [self locationOfElementAtIndex:to]; + return [self subexpressionToLocation:toLocation]; +} + +- (MPExpression *)subexpressionWithIndexedRange:(NSRange)range +{ + NSRange locationRange = [self rangeForIndexedRange:range]; + return [self subexpressionWithRange:locationRange]; } -#pragma mark Working With Expressions - (MPExpression *)subexpressionFromLocation:(NSUInteger)from { return [self subexpressionWithRange:NSMakeRange(from, self.length - from)]; @@ -446,7 +539,13 @@ return subexpression; } -#pragma mark Mutating Expressions +- (void)replaceElementsInIndexedRange:(NSRange)range + withElements:(NSArray *)elements +{ + [self replaceSymbolsInRange:[self rangeForIndexedRange:range] + withElements:elements]; +} + - (void)appendElement:(id)anElement { [self appendElements:@[anElement]]; @@ -457,6 +556,20 @@ [self replaceSymbolsInRange:NSMakeRange(self.length, 0) withElements:elements]; } +- (void)insertElement:(id)anElement + atIndex:(NSUInteger)index +{ + [self insertElement:anElement + atLocation:[self locationOfElementAtIndex:index]]; +} + +- (void)insertElements:(NSArray *)elements + atIndex:(NSUInteger)index +{ + [self insertElements:elements + atLocation:[self locationOfElementAtIndex:index]]; +} + - (void)insertElement:(id)anElement atLocation:(NSUInteger)location { @@ -469,9 +582,20 @@ [self replaceSymbolsInRange:NSMakeRange(location, 0) withElements:elements]; } +- (void)deleteElementsInIndexedRange:(NSRange)range +{ + [self deleteElementsInIndexedRange:[self rangeForIndexedRange:range]]; +} + - (void)deleteElementsInRange:(NSRange)range { [self replaceSymbolsInRange:range withElements:@[]]; } +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx +{ + [self replaceSymbolsInRange:NSMakeRange(idx, 1) + withElements:@[obj]]; +} + @end diff --git a/MathPad/MPFunction.m b/MathPad/MPFunction.m index 0ae055e..141c56b 100644 --- a/MathPad/MPFunction.m +++ b/MathPad/MPFunction.m @@ -93,7 +93,7 @@ { NSUInteger selfIndex = [self.parent indexOfElement:self]; MPRangePath *newPath = MPMakeRangePath([rangePath.location indexPathByPreceedingIndex:selfIndex], rangePath.length); - [self.parent didChangeElementsInRangePath:newPath + [self.parent didChangeElementsInIndexedRangePath:newPath replacementLength:replacementLength]; }