// // MPFunction.h // MathPad // // Created by Kim Wittenburg on 18.04.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPExpression.h" /*! @header MPFunction is a half abstract class that is designed to be subclassed. Subclasses of the MPFunction class (subsequently called functions) need to regard the following guidelines: 1. In their header file functions should declare readwrite, nonatomic properties for every child (subexpression) they have. 2. For every public child accessor there should be one use of the @link MPFunctionAccessorImplementation@/link macro in the implementation file. See the documentation on the macro for details. 3. Functions must override the @link childrenAccessors@/link method returning the KVC compatible names of the publicly declared property accessors 4. Functions must also override the @link functionTermClass@/link method returning a Class that can evaluate the function. See the documentation on that method for details. 5. Optionally functions can implement @link expectsVariableDefinitionInChildAtIndex:@/link if one of its children is supposed to contain a variable definition. 6. Functions should also override the NSObject method description. Although this is not a requirement it is strongly recommended. Functions play an important role in the expression tree. For details on the that subject see the documentation for @link MPExpression@/link. @note None of the children of a function may be nil at any time. */ /*! @define MPFunctionAccessorImplementation @abstract This macro implements the setter of a previously declared property of a MPFunction subclass. @discussion Overriding the default setter implementation is necessary to maintain the consistency of the expression tree. Not implementing a custom setter using this macro or a similar custom method will lead to unexpected behaviour. If you need to implement additional custom behaviour in a property setter it is recommended that you copy the content of the macro. @param Accessor The capitalized name of the property to be implemented. This is used for the name of the method: set.... @param variableName The name of the instance variable that backs the property. Normally this is the same as the name of the property prefixed with an underscore. */ #define MPFunctionAccessorImplementation(Accessor, variableName) \ - (void)set##Accessor:(MPExpression *)value \ { \ variableName.parent = nil; \ variableName = value; \ variableName.parent = self; \ [self didChangeChildAtIndex:[self indexOfChild:variableName]]; \ } @class MPFunction, MPExpression, MPRangePath; @protocol MPExpressionElement; /*! @class MPFunction @abstract The MPFunction class represents a mathematical function. @discussion A mathematical function can be anything that exceeds the graphical capability of text. Functions like sin, cos or ln are not considered functions in this context because they can be represented by text. */ @interface MPFunction : NSObject #pragma mark Working With the Expression Tree /*! @methodgroup Working With the Expression Tree */ /*! @property parent @abstract The receiver's parent. @discussion Expressions and functions are organized in a tree-like structure. Through this property a function's containing expression can be accessed. @warning Do not set this property manually. It will automatically be set when the function is added to or removed from an expression. @note If you need to know when a function is added to or removed from an expression you can observe this property using KVO. @return The parent of the receiver or nil if the receiver does not have a parent. */ @property (nonatomic, weak) MPExpression *parent; /*! @method rootExpression @abstract 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. A root expression does not have a parent. @return The root expression from the receiver's expression tree or nil if this function does not have a parent. */ - (MPExpression *)rootExpression; /*! @method indexPath @abstract Returns the index path of the receiver in the expression tree. @discussion The index path contains the indexes (starting from the root expression of the receiver's expression tree) that lead to the receiver. The indexes are expressed in the element reference frame. @return The index path of the receiver in the expression tree or nil if this function does not have a parent. */ - (NSIndexPath *)indexPath; /*! @method numberOfChildren @abstract Returns the number of children the receiver has. @return The number of children the receiving function has. */ - (NSUInteger)numberOfChildren; /*! @method childAtIndex: @abstract Returns the child at the specified index. @discussion The ordering of children is determined by the order of the child accessors as returned from the @link childrenAccessors@/link method. @param index The index of the requested child. @return The expression that corresponds to the child at index. */ - (MPExpression *)childAtIndex:(NSUInteger)index; /*! @method setChild:atIndex: @abstract Sets the child at the specified index. @discussion Although this method does not call the respective accessor methods it behaves exactly as the setter method implemented using the @link MPFunctionAccessorImplementation@/link macro. @param child The child that should replace the previous one. @param index The index of the child to be replaced. */ - (void)setChild:(MPExpression *)child atIndex:(NSUInteger)index; /*! @method children @abstract Returns the receiver's children. @discussion The ordering of the children is the same as in the array returned from @link childrenAccessors@/link. @return An array containing all the function's children. */ - (NSArray *)children; /*! @method indexOfChild: @abstract Returns the index of child in the receiver. @param child The child to be searched. @return The index of child or NSNotFound if none of the receiver's children is equal to child. */ - (NSUInteger)indexOfChild:(MPExpression *)child; /*! @method elementAtIndexPath: @abstract Returns the element at the specified index path. @discussion This method finds the elements at the respective indexes starting at the root expression from the receiver's expression tree. The returned object can be a NSString, a MPFunction or a @link MPExpression@/link instance depending on the specified indexPath. If any of the indexes exceed the bounds of the respective receiver a 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 indexes are expressed in the element reference frame. @return The element located at indexPath. The element is not copied before it is returned. Be aware of the fact that any mutations made to the returned object will update the receiver's expression tree to reflect the change. */ - (id)elementAtIndexPath:(NSIndexPath *)indexPath; #pragma mark Notifications /*! @methodgroup Notifications */ /*! @method didChangeElementsInRangePath:replacementLength: @abstract This Notification message is sent automatically from the receiver to itself if one of its children was mutated or changed. @discussion This method should also be called when one of the children is changed via one of the accessor methods. @param rangePath The location of the change that happened. Because it may have happened somewhere deep down in the expression tree a range path is used. @param replacementLength The number of elements that replaced the elements addressed by the range path. */ - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)replacementLength; /*! @method didChangeChildAtIndex: @abstract Convenience method that calls @link didChangeElementsInRangePath:replacementLength:@/link @discussion This method is automatically called when one of the children is changed using one of the accessor methods or @link setChild:atIndex:@/link. @param index The index of the child that was changed. */ - (void)didChangeChildAtIndex:(NSUInteger)index; #pragma mark Evaluating Functions /*! @methodgroup Evaluating Functions */ /*! @method expectsVariableDefinitionInChildAtIndex: @abstract Returns whether the child at anIndex is supposed to contain a variable definition. @discussion This method is automatically called during the parsing process. You can override this method to opt into the default behaviour. If you override this method you do not need to call super. The default implementation just returns NO for every index. @param anIndex The index of the child to check. @return YES if the child at anIndex is supposed to contain a variable definition, NO otherwise. */ - (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)anIndex; @end /*! @category MPFunction (MPSubclassOverride) @abstract The methods in this category must be implemented by any concrete subclasses of MPFunction. @discussion MPFunction does not implement the functions itself but calls them at various points. If a subclass does not provide implementations your app will most likely crash. */ @interface MPFunction (MPSubclassOverride) #pragma mark Working With the Expression Tree /*! @methodgroup Working With the Expression Tree */ /*! @method childrenAccessors @abstract Returns the names of the children properties as strings. @discussion All objects in the returned array must be strings. @return An array of strings describing the names of children a function has. */ - (NSArray *)childrenAccessors; #pragma mark Evaluating Functions /*! @methodgroup Evaluating Functions */ /*! @method functionTermClass @abstract Returns the class that represents the receiver's evaluation behaviour. @discussion Subclasses of MPFunction can (like @link MPExpression@/link) not evaluate themselves. Instead this method is called on every function. The returned class must be a valid subclass of @link //apple_ref/occ/cl/MPFunctionTerm@/link. It will be instanciated using the init method. @return The @link //apple_ref/occ/cl/MPFunctionTerm@/link subclass that can evaluate instances of the receiver's class. */ - (Class)functionTermClass; @end