Added Documentation
This commit is contained in:
@@ -6,11 +6,65 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@const MPIllegalElementException
|
||||
@brief Name for an exception that is raised if an invalid element is
|
||||
added to an expression.
|
||||
|
||||
@discussion This exception may be raised during initialization of an
|
||||
expression or when the expression is mutated. This exception
|
||||
always contains the @c MPIllegalElementExceptionElementKey key in
|
||||
its @c userInfo dictionary.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
|
||||
|
||||
|
||||
/*!
|
||||
@const MPIllegalElementExceptionElementKey
|
||||
@brief Predefined key for the invalid element that caused an exception
|
||||
to be raised.
|
||||
|
||||
@discussion The invalid element can be of any type.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const MPMathKitErrorDomain;
|
||||
FOUNDATION_EXPORT NSString *const MPExpressionPathErrorKey;
|
||||
|
||||
/*!
|
||||
@const MPMathKitErrorDomain
|
||||
@brief Predefined error domain for errors from the MathKit framework.
|
||||
|
||||
@discussion Errors in MathKit can occur during parsing of expressions or
|
||||
during evaluation of expressions. These two can be distinguished
|
||||
by the error code. Parsing errors have lower error codes.
|
||||
Evaluation errors (math errors) have error codes from @c 100 upwards.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPMathKitErrorDomain;
|
||||
|
||||
|
||||
// TODO: Deal with this:
|
||||
// FOUNDATION_EXPORT NSString *const MPPathToExpressionKey;
|
||||
// FOUNDATION_EXPORT NSString *const MPRangeKey;
|
||||
|
||||
|
||||
/*!
|
||||
@function MPParseError
|
||||
@brief Creates an @c NSError object in the @c MPMathKitErrorDomain.
|
||||
|
||||
@discussion The created error is filled with the passed data if present.
|
||||
|
||||
@param errorCode
|
||||
The error code of the created error.
|
||||
|
||||
@param errorDescription
|
||||
The localized description of the created error. May be @c nil.
|
||||
|
||||
@param underlyingError
|
||||
The underlying error of the created error. May be @c nil.
|
||||
|
||||
@return An @c NSError object initialized with the given data.
|
||||
*/
|
||||
NS_INLINE NSError * MPParseError(NSInteger errorCode,
|
||||
NSString *errorDescription,
|
||||
NSError *underlyingError) {
|
||||
@@ -27,12 +81,41 @@ NS_INLINE NSError * MPParseError(NSInteger errorCode,
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@typedef MPReferenceFrame
|
||||
@brief A reference frame describes what an @em item is.
|
||||
|
||||
@discussion An item may be an element, a symbol or a token. A symbol is the
|
||||
smallest possible size for an item. An element as well as a token
|
||||
consist of one or more symbols. Similarly an element consists of
|
||||
one or more tokens.
|
||||
|
||||
@constant MPElementReferenceFrame
|
||||
Specifies that items should be interpreted as elements. An
|
||||
element is either a @c NSString object or a @c MPFunction object.
|
||||
|
||||
@constant MPSymbolReferenceFrame
|
||||
Specifies that items should be interpreted as symbols. A symbol
|
||||
can be a single character (as they are for example typed in by
|
||||
the user) or a @c MPFunction object.
|
||||
|
||||
@constant MPTokenReferenceFrame
|
||||
Specifies that items should be interpreted as tokens. A token is
|
||||
a logical unit of content in an expression. This can be a single
|
||||
character (e.g. '+'), an arbitrary number of characters (e.g. a
|
||||
number) or a @c MPFunction object.
|
||||
|
||||
All tokens must conform to the @c MPToken protocol.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
MPElementReferenceFrame,
|
||||
MPSymbolReferenceFrame,
|
||||
MPTokenReferenceFrame
|
||||
};
|
||||
|
||||
|
||||
|
||||
@class MPExpression, MPFunction, MPRangePath, MPExpressionTree, MPExpressionEvaluator;
|
||||
@protocol MPExpressionElement, MPToken;
|
||||
|
||||
@@ -42,45 +125,28 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
expression.
|
||||
|
||||
@discussion Every expression consists of string elements (represented by the
|
||||
@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.
|
||||
@c NSString class) and function elements (represented by the @c
|
||||
MPFunction class). Functions likewise can have expressions as
|
||||
elements (also called @em 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'.
|
||||
|
||||
Through this organization expression are organized in a tree-like
|
||||
structure (called the 'expression tree') allowing easy and
|
||||
logical access to each element.
|
||||
@note There is also the @c MPExpressionTree class. That class provides
|
||||
an alternative representation of an expression. The 'expression
|
||||
tree' does not refer to that class.
|
||||
|
||||
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.
|
||||
correctly or could not be evaluated.
|
||||
*/
|
||||
@interface MPExpression : NSObject <NSCopying, NSCoding>
|
||||
|
||||
|
||||
#pragma mark Creation Methods
|
||||
/*!
|
||||
@methodgroup Creation Methods
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@@ -128,12 +194,15 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
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.
|
||||
@return An expression containing @c elements.
|
||||
*/
|
||||
- (instancetype)initWithElements:(NSArray *)elements; /* designated initializer */
|
||||
|
||||
|
||||
#pragma mark Querying Expressions
|
||||
/*!
|
||||
@methodgroup Querying Expressions
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@@ -142,8 +211,9 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
|
||||
@discussion Expressions are organized in a tree-like structure. Through this
|
||||
property an expression's containing function can be accessed.
|
||||
@warning You should not set this property manually unless you are
|
||||
implementing a subclass of @c MPFunction.
|
||||
@warning You should never set this property manually. If you are
|
||||
implementing a custom subclass of @c MPFunction use the @c
|
||||
MPFunctionAccessorImplementation macro.
|
||||
|
||||
@return The parent of the receiver or @c nil if the receiver is the root
|
||||
expression.
|
||||
@@ -170,7 +240,7 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
|
||||
@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
|
||||
expressed in the element reference frame. If any of the indexes
|
||||
exceed the respective receiver's bounds a @c NSRangeException is
|
||||
raised.
|
||||
|
||||
@@ -180,41 +250,62 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
|
||||
|
||||
/*!
|
||||
@method numberOfElements
|
||||
@brief Returns the number of elements in the receiver.
|
||||
@method countItemsInReferenceFrame:
|
||||
@brief Returns the number of items in the receiver in the respective
|
||||
reference frame.
|
||||
|
||||
@discussion The number of elements may vary from the number of elements that
|
||||
were added to the receiver (using either @c -initWithElements: or
|
||||
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).
|
||||
@param referenceFrame
|
||||
The reference frame that should be used to count the items.
|
||||
|
||||
@return The current number of elements in the receiver.
|
||||
@return The current number of items in the receiver counted in the given
|
||||
reference frame.
|
||||
*/
|
||||
- (NSUInteger)countItemsInReferenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method elementAtIndex:
|
||||
@brief Returns the element at @c anIndex.
|
||||
@method itemAtIndex:referenceFrame:
|
||||
@brief Returns the item at @c anIndex.
|
||||
|
||||
@discussion The element is not copied before it is returned. So be aware that
|
||||
@discussion The item is not copied before it is returned. So be aware that
|
||||
if you mutate a @c MPFunction object returned from this function
|
||||
the changes will be reflected in the receiver.
|
||||
|
||||
@note This method can also be called using indexed subscript
|
||||
getter syntax.
|
||||
|
||||
@param anIndex
|
||||
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.
|
||||
The index of the item. If the index is greater than the number of
|
||||
items for the respective reference frame an @c NSRangeException
|
||||
is raised.
|
||||
|
||||
@return The element at @c anIndex.
|
||||
@param referenceFrame
|
||||
The reference frame that should be used to identify the item at
|
||||
@c anIndex.
|
||||
|
||||
@return The item at @c anIndex.
|
||||
*/
|
||||
- (id)itemAtIndex:(NSUInteger)anIndex
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method elementAtIndex:referenceFrame:
|
||||
@brief Returns the element at the specified index.
|
||||
|
||||
@discussion An element is either a string or a function. If @c anIndex
|
||||
specifies a position inside a string the complete string element
|
||||
is returned.
|
||||
|
||||
This method does not return any item in any reference frame but
|
||||
only complete elements. Use @c -itemAtIndex:referenceFrame: for
|
||||
that purpose.
|
||||
|
||||
@param anIndex
|
||||
The index of the element.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c anIndex is specified in.
|
||||
|
||||
@return The item at @c anIndex.
|
||||
*/
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
@@ -226,45 +317,48 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
@param element
|
||||
The element to find.
|
||||
|
||||
@return The index of @c element expressed in the indexed reference frame.
|
||||
@return The index of @c element expressed in the element reference frame.
|
||||
*/
|
||||
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element;
|
||||
|
||||
|
||||
/*!
|
||||
@method elementsInIndexedRange:
|
||||
@brief Returns an array of the elements that are located in the
|
||||
specified range. The range is specified in the indexed reference
|
||||
frame.
|
||||
@method itemsInRange:referenceFrame:
|
||||
@brief Returns an array of the items that are located in the specified
|
||||
range.
|
||||
|
||||
@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
|
||||
returned element will be reflected in the receiver.
|
||||
returned objects will be reflected in the receiver.
|
||||
|
||||
If the @c range exceeds the receiver's bounds an @c
|
||||
NSRangeException is raised.
|
||||
|
||||
@param range
|
||||
The requested range within the receiver's bounds expressed in the
|
||||
indexed reference frame.
|
||||
@param aRange
|
||||
The requested range within the receiver's bounds.
|
||||
|
||||
@param referenceFrame
|
||||
The referenceFrame @c aRange is expressed in.
|
||||
|
||||
@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 *)itemsInRange:(NSRange)range
|
||||
- (NSArray *)itemsInRange:(NSRange)aRange
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method elements
|
||||
@brief Returns an array of all elements in the receiver.
|
||||
@method allItemsInReferenceFrame:
|
||||
@brief Returns an array of all items in the receiver for the specified
|
||||
reference frame.
|
||||
|
||||
@discussion The elements in the returned array are not copied before they are
|
||||
returned.
|
||||
returned so be aware that mutations to any of the returned
|
||||
objects will be reflected in the receiver.
|
||||
|
||||
@return An array of all elements from the receiver.
|
||||
@return An array of all items in the receiver.
|
||||
*/
|
||||
- (NSArray *)allItemsInReferenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
@@ -285,7 +379,7 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
|
||||
@param indexPath
|
||||
The index path the required object is located at. The indexes are
|
||||
expressed in the indexed reference frame.
|
||||
expressed in the element 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
|
||||
@@ -293,19 +387,127 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
*/
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (NSUInteger)convertIndex:(NSUInteger)index
|
||||
|
||||
/*!
|
||||
@method convertIndex:fromReferenceFrame:toReferenceFrame:
|
||||
@brief Converts an index from one reference frame to another.
|
||||
|
||||
@discussion This method ignores any offsets into items the conversion between
|
||||
reference frames can cause. If you are interested in the offset
|
||||
use @c -convertIndex:fromReferenceFrame:toReferenceFrame:offset:
|
||||
instead.
|
||||
|
||||
@param anIndex
|
||||
The index to be converted.
|
||||
|
||||
@param fromReferenceFrame
|
||||
The reference frame @c anIndex is specified in.
|
||||
|
||||
@param toReferenceFrame
|
||||
The reference frame @c anIndex should be converted to.
|
||||
|
||||
@return An index specified in the @c toReferenceFrame. If @c anIndex
|
||||
specifies a location inside an item in the @c toReferenceFrame
|
||||
the index of the corresponding item is returned.
|
||||
*/
|
||||
- (NSUInteger)convertIndex:(NSUInteger)anIndex
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame;
|
||||
|
||||
- (NSUInteger)convertIndex:(NSUInteger)index
|
||||
|
||||
/*!
|
||||
@method convertIndex:fromReferenceFrame:toReferenceFrame:offset:
|
||||
@brief Converts an index from one reference frame to another.
|
||||
|
||||
@discussion If @c anIndex specifies a location inside an item in the @c
|
||||
toReferenceFrame the index of the corresponding item is returned.
|
||||
|
||||
@param anIndex
|
||||
The index to be converted.
|
||||
|
||||
@param fromReferenceFrame
|
||||
The reference frame @c anIndex is specified in.
|
||||
|
||||
@param toReferenceFrame
|
||||
The reference frame @c anIndex should be converted to.
|
||||
|
||||
@param offset
|
||||
This output parameter will be set to the number of symbols that
|
||||
are between the location specified by @c anIndex and the index
|
||||
that is actually returned. The offset is specified in the symbol
|
||||
reference frame. If you are not interested in the offset pass
|
||||
@c NULL.
|
||||
|
||||
@return An index specified in the @c toReferenceFrame corresponding to
|
||||
@c anIndex.
|
||||
*/
|
||||
- (NSUInteger)convertIndex:(NSUInteger)anIndex
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
||||
offset:(NSUInteger *)offset;
|
||||
offset:(out NSUInteger *)offset;
|
||||
|
||||
|
||||
/*!
|
||||
@method convertRange:fromReferenceFrame:toReferenceFrame:
|
||||
@brief Converts a range from one reference frame to another.
|
||||
|
||||
@discussion This method just converts the location and target of the range
|
||||
separately using @c
|
||||
-convertIndex:fromReferenceFrame:toReferenceFrame:. This method
|
||||
ensures that the returned range definitely includes all items
|
||||
that were specified by @c aRange.
|
||||
|
||||
This method ignores the possibility that the returned range may
|
||||
include more than @a aRange. If you need that information use
|
||||
@c -convertRange:fromReferenceFrame:toReferenceFrame:leadingOffset:trailingOffset:.
|
||||
|
||||
@param aRange
|
||||
The range to be converted.
|
||||
|
||||
@param fromReferenceFrame
|
||||
The reference frame @c aRange is specified in.
|
||||
|
||||
@param toReferenceFrame
|
||||
The reference frame @c aRange is to be converted to.
|
||||
|
||||
@return A range in the @c toReferenceFrame that includes the the items
|
||||
specified by @c aRange.
|
||||
*/
|
||||
- (NSRange)convertRange:(NSRange)aRange
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method convertRange:fromReferenceFrame:toReferenceFrame:leadingOffset:trailingOffset:
|
||||
@brief Converts a range from one reference frame to another.
|
||||
|
||||
@discussion This method just converts the location and target of the range
|
||||
separately using @c
|
||||
-convertIndex:fromReferenceFrame:toReferenceFrame:. This method
|
||||
ensures that the returned range definitely includes all items
|
||||
that were specified by @c aRange.
|
||||
|
||||
@param aRange
|
||||
The range to be converted.
|
||||
|
||||
@param fromReferenceFrame
|
||||
The reference frame @c aRange is specified in.
|
||||
|
||||
@param toReferenceFrame
|
||||
The reference frame @c aRange is to be converted to.
|
||||
|
||||
@param leadingOffset
|
||||
The offset of the location of the returned range in respect to
|
||||
the location of @c aRange.
|
||||
|
||||
@param trailingOffset
|
||||
The offset of the last index in the range in respect to the last
|
||||
index of @c aRange.
|
||||
|
||||
@return A range in the @c toReferenceFrame that includes the the items
|
||||
specified by @c aRange.
|
||||
*/
|
||||
- (NSRange)convertRange:(NSRange)aRange
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
||||
@@ -314,17 +516,19 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
|
||||
|
||||
#pragma mark Mutating Expressions
|
||||
/*!
|
||||
@methodgroup Mutating Expressions
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method replaceSymbolsInRange:withElements:
|
||||
@method replaceItemsInRange:referenceFrame: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
|
||||
@@ -333,35 +537,98 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
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 aRange
|
||||
The @c range of symbols (including functions) to replace.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c aRange is specified in.
|
||||
|
||||
@param elements
|
||||
The elements that should replace the symbols specified by @c
|
||||
range.
|
||||
*/
|
||||
- (void)replaceItemsInRange:(NSRange)range
|
||||
- (void)replaceItemsInRange:(NSRange)aRange
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
withElements:(NSArray *)elements;
|
||||
|
||||
|
||||
/*!
|
||||
@method subexpressionFromIndex:referenceFrame:
|
||||
@brief Creates an expression from the items in the receiver from the
|
||||
specified index to the end.
|
||||
|
||||
@discussion The items from the receiver are copied. Mutations to the returned
|
||||
expression will not change the receiver.
|
||||
|
||||
@param from
|
||||
The index from which to start constructing the subexpression.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c from is specified in.
|
||||
|
||||
@return An expression containing the items from the given index to the
|
||||
end.
|
||||
*/
|
||||
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method subexpressionToIndex:referenceFrame:
|
||||
@brief Creates an expression from the items in the receiver from the
|
||||
first item to the end.
|
||||
|
||||
@discussion The items from the receiver are copied. Mutations to the returned
|
||||
expression will not change the receiver.
|
||||
|
||||
@param to
|
||||
The index of the first element not to include in the newly
|
||||
constructed subexpression.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c to is specified in.
|
||||
|
||||
@return An expression containing the items from the first item to the
|
||||
given index.
|
||||
*/
|
||||
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)range
|
||||
|
||||
|
||||
/*!
|
||||
@method subexpressionWithRange:referenceFrame:
|
||||
@brief Creates an expression from the items in the receiver within the
|
||||
specified range.
|
||||
|
||||
@discussion The items from the receiver are copied. Mutations to the returned
|
||||
expression will not change the receiver.
|
||||
|
||||
@param aRange
|
||||
Specifies the items to be included in the newly created
|
||||
subexpression.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c aRange is specified in.
|
||||
|
||||
@return An expression containing the items in @c aRange.
|
||||
*/
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)aRange
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
#pragma mark Evaluating Expressions
|
||||
/*!
|
||||
@methodgroup Evaluating Expressions
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method evaluateWitError:
|
||||
@brief Evaluates the receiving expression.
|
||||
@brief Evaluates the receiver.
|
||||
|
||||
@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.
|
||||
want more control over the evaluation process use @c -parse
|
||||
instead.
|
||||
|
||||
@param error
|
||||
If the receiver (or any of its elements) contains a syntax error
|
||||
@@ -371,18 +638,36 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
|
||||
@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.
|
||||
appropriate value. If @c NaN is returned it means there was a
|
||||
math error. In most cases the error parameter contains an
|
||||
appropriate description of the problem.
|
||||
*/
|
||||
- (NSDecimalNumber *)evaluateWithError:(NSError *__autoreleasing *)error;
|
||||
|
||||
|
||||
/*!
|
||||
@method parse
|
||||
@brief Parses the receiver.
|
||||
|
||||
@discussion This method returns a valid object even if the expression
|
||||
contains syntax errors. Send the returned object a @c -validate:
|
||||
message to check for syntax errors before you evaluate it. For
|
||||
more information on evaluation see the documentation of @c
|
||||
MPExpressionTree.
|
||||
|
||||
@return A @c MPExpressionTree object that can evaluate the receiver.
|
||||
*/
|
||||
- (MPExpressionTree *)parse;
|
||||
|
||||
|
||||
#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
|
||||
/*!
|
||||
@methodgroup Notifications
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method didChangeElementsInIndexedRangePath:replacementLength:
|
||||
@method didChangeElementsInRangePath:replacementLength:
|
||||
@brief Called after the receiver has been mutated.
|
||||
|
||||
@discussion This method does nothing more than notify it's parent that it has
|
||||
@@ -395,77 +680,165 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
||||
@param rangePath
|
||||
The range path at which the receiver was changed starting at the
|
||||
receiver. The range addressed by @c rangePath is expressed in the
|
||||
indexed reference frame.
|
||||
element reference frame.
|
||||
|
||||
@param replacementLength
|
||||
The number of elements replacing the elements specified by @c
|
||||
rangePath.
|
||||
rangePath (also specified in the element reference frame).
|
||||
*/
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
|
||||
|
||||
#pragma mark Basic NSObject Methods
|
||||
// TODO: Check this
|
||||
// - (BOOL)isEqualToExpression:(MPExpression *)anExpression;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface MPExpression (MPExpressionConvenience)
|
||||
|
||||
|
||||
#pragma mark Querying Expressions
|
||||
/*!
|
||||
@methodgroup Querying Expressions
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method countElements
|
||||
@brief Returns the number of elements in the receiver.
|
||||
|
||||
@return The number of elements in the receiver expressed in the element
|
||||
reference frame.
|
||||
*/
|
||||
- (NSUInteger)countElements;
|
||||
|
||||
|
||||
/*!
|
||||
@method countSymbols
|
||||
@brief Returns the number of symbols in the receiver.
|
||||
|
||||
@return The number of symbols in the receiver expressed in the symbol
|
||||
reference frame.
|
||||
*/
|
||||
- (NSUInteger)countSymbols;
|
||||
|
||||
|
||||
/*!
|
||||
@method countTokens
|
||||
@brief Returns the number of tokens in the receiver.
|
||||
|
||||
@return The number of tokens in the receiver expressed in the token
|
||||
reference frame.
|
||||
*/
|
||||
- (NSUInteger)countTokens;
|
||||
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index;
|
||||
- (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)index;
|
||||
- (id<MPToken>)tokenAtIndex:(NSUInteger)index;
|
||||
|
||||
/*!
|
||||
@method elementAtIndex:
|
||||
@brief Returns the element at the specified index.
|
||||
|
||||
@param anIndex
|
||||
The index of the element specified in the element reference
|
||||
frame.
|
||||
|
||||
@return The element at @c anIndex.
|
||||
*/
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex;
|
||||
|
||||
|
||||
/*!
|
||||
@method symbolAtIndex:
|
||||
@brief Returns the symbol at the specified index.
|
||||
|
||||
@param anIndex
|
||||
The index of the symbol specified in the symbol reference frame.
|
||||
|
||||
@return The symbol at @c anIndex.
|
||||
*/
|
||||
- (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)anIndex;
|
||||
|
||||
|
||||
/*!
|
||||
@method tokenAtIndex:
|
||||
@brief Returns the token at the specified index.
|
||||
|
||||
@param anIndex
|
||||
The index of the token specified in the token reference frame.
|
||||
|
||||
@return The token at @c anIndex.
|
||||
*/
|
||||
- (id<MPToken>)tokenAtIndex:(NSUInteger)anIndex;
|
||||
|
||||
|
||||
#pragma mark Mutating Expressions
|
||||
/*!
|
||||
@methodgroup Mutating Expressions
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method appendElement:
|
||||
@brief Appends @c anElement to the receiver.
|
||||
@method appendElement:
|
||||
@brief Appends @c anElement to the receiver.
|
||||
|
||||
@param anElement
|
||||
The element to append to the receiver.
|
||||
@param anElement
|
||||
The element to append to the receiver.
|
||||
*/
|
||||
- (void)appendElement:(id<MPExpressionElement>)anElement;
|
||||
|
||||
|
||||
/*!
|
||||
@method appendElements:
|
||||
@brief Appends the objects from @c elements to the receiver.
|
||||
@method appendElements:
|
||||
@brief Appends the objects from @c elements to the receiver.
|
||||
|
||||
@param elements
|
||||
The elements to append to the receiver.
|
||||
@param elements
|
||||
The elements to append to the receiver.
|
||||
*/
|
||||
- (void)appendElements:(NSArray *)elements;
|
||||
|
||||
|
||||
/*!
|
||||
@method insertElement:atIndex:referenceFrame:
|
||||
@brief Inserts @c anElement at the specified index.
|
||||
|
||||
@param anElement
|
||||
The element to be inserted.
|
||||
|
||||
@param index
|
||||
The index where to insert @c anElement.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c index is specified in.
|
||||
*/
|
||||
- (void)insertElement:(id<MPExpressionElement>)anElement
|
||||
atIndex:(NSUInteger)index
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method insertElements:atIndex:referenceFrame:
|
||||
@brief Inserts @c elements at the specified index.
|
||||
|
||||
@param elements
|
||||
The elements to be inserted.
|
||||
|
||||
@param index
|
||||
The index where to insert @c elements.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame @c index is specified in.
|
||||
*/
|
||||
- (void)insertElements:(NSArray *)elements
|
||||
atIndex:(NSUInteger)index
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method deleteElementsInRange:
|
||||
@brief Removes the elements specified by @c range from the receiver.
|
||||
@method deleteElementsInRange:
|
||||
@brief Removes the elements specified by @c range from the receiver.
|
||||
|
||||
@discussion The range is specified in the length reference frame.
|
||||
@param range
|
||||
The range of items to remove from the receiver.
|
||||
|
||||
If @c range exceeds the receiver's bounds a @c NSRangeException
|
||||
is raised.
|
||||
|
||||
@param range
|
||||
The range to remove from the receiver.
|
||||
@param referenceFrame
|
||||
The reference frame @c range is specified in.
|
||||
*/
|
||||
- (void)deleteElementsInRange:(NSRange)range
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
|
||||
|
||||
NSString *const MPIllegalElementException = @"MPIllegalElementException";
|
||||
NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptionElementKey";
|
||||
|
||||
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
|
||||
NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
|
||||
|
||||
|
||||
@interface MPExpression () {
|
||||
NSMutableArray * _elements;
|
||||
}
|
||||
@@ -62,11 +66,13 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return [self initWithElements:@[]];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithElement:(id<MPExpressionElement>)element
|
||||
{
|
||||
return [self initWithElements:@[element]];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithElements:(NSArray *)elements
|
||||
{
|
||||
self = [super init];
|
||||
@@ -96,7 +102,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
{
|
||||
for (id element in elements) {
|
||||
if (![element conformsToProtocol:@protocol(MPExpressionElement)]) {
|
||||
@throw [NSException exceptionWithName:@"MPIllegalElementException"
|
||||
@throw [NSException exceptionWithName:MPIllegalElementException
|
||||
reason:@"Elements must conform to the MPExpressionElement protocol."
|
||||
userInfo:@{MPIllegalElementExceptionElementKey: element}];
|
||||
}
|
||||
@@ -141,6 +147,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Querying Expressions
|
||||
|
||||
|
||||
@@ -152,6 +159,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return [self.parent rootExpression];
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPath
|
||||
{
|
||||
if (self.parent) {
|
||||
@@ -162,6 +170,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)countItemsInReferenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
switch (referenceFrame) {
|
||||
@@ -182,6 +191,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (id)itemAtIndex:(NSUInteger)anIndex
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
@@ -210,6 +220,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
@@ -219,20 +230,23 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return _elements[elementIndex];
|
||||
}
|
||||
|
||||
|
||||
#warning If multiple equal expressions exist errors may occur...
|
||||
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
|
||||
{
|
||||
return [_elements indexOfObject:element];
|
||||
}
|
||||
|
||||
- (NSArray *)itemsInRange:(NSRange)range
|
||||
|
||||
- (NSArray *)itemsInRange:(NSRange)aRange
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
MPExpression *subexpression = [self subexpressionWithRange:range
|
||||
MPExpression *subexpression = [self subexpressionWithRange:aRange
|
||||
referenceFrame:referenceFrame];
|
||||
return [subexpression allItemsInReferenceFrame:referenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)allItemsInReferenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
switch (referenceFrame) {
|
||||
@@ -259,6 +273,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.length == 0) {
|
||||
@@ -275,26 +290,28 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSUInteger)convertIndex:(NSUInteger)index
|
||||
|
||||
- (NSUInteger)convertIndex:(NSUInteger)anIndex
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
||||
{
|
||||
return [self convertIndex:index
|
||||
return [self convertIndex:anIndex
|
||||
fromReferenceFrame:fromReferenceFrame
|
||||
toReferenceFrame:toReferenceFrame
|
||||
offset:NULL];
|
||||
}
|
||||
|
||||
- (NSUInteger)convertIndex:(NSUInteger)index
|
||||
|
||||
- (NSUInteger)convertIndex:(NSUInteger)anIndex
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
||||
offset:(NSUInteger *)offset
|
||||
offset:(out NSUInteger *)offset
|
||||
{
|
||||
if (fromReferenceFrame == toReferenceFrame || index == 0) {
|
||||
if (fromReferenceFrame == toReferenceFrame || anIndex == 0) {
|
||||
if (offset) {
|
||||
*offset = 0;
|
||||
}
|
||||
return index;
|
||||
return anIndex;
|
||||
}
|
||||
|
||||
NSUInteger symbolIndex __block = 0;
|
||||
@@ -302,16 +319,16 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
case MPElementReferenceFrame:
|
||||
[_elements enumerateObjectsUsingBlock:^(id<MPExpressionElement> obj, NSUInteger idx, BOOL *stop) {
|
||||
symbolIndex += obj.length;
|
||||
*stop = idx >= index - 1;
|
||||
*stop = idx >= anIndex - 1;
|
||||
}];
|
||||
break;
|
||||
|
||||
case MPSymbolReferenceFrame:
|
||||
symbolIndex = index;
|
||||
symbolIndex = anIndex;
|
||||
break;
|
||||
|
||||
case MPTokenReferenceFrame:
|
||||
symbolIndex = [self.tokens[index] range].location;
|
||||
symbolIndex = [self.tokens[anIndex] range].location;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -351,7 +368,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
if (NSMaxRange(token.range) < symbolIndex || token.range.location > symbolIndex) {
|
||||
continue;
|
||||
}
|
||||
NSUInteger offsetInToken = index - token.range.location;
|
||||
NSUInteger offsetInToken = anIndex - token.range.location;
|
||||
if (offsetInToken == token.range.length) {
|
||||
offsetInToken = 0;
|
||||
tokenIndex++;
|
||||
@@ -367,6 +384,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSRange)convertRange:(NSRange)aRange
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
||||
@@ -378,6 +396,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
trailingOffset:NULL];
|
||||
}
|
||||
|
||||
|
||||
- (NSRange)convertRange:(NSRange)aRange
|
||||
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
||||
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
||||
@@ -399,20 +418,21 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
#pragma mark Mutating Expressions
|
||||
|
||||
|
||||
- (void)replaceItemsInRange:(NSRange)range
|
||||
- (void)replaceItemsInRange:(NSRange)aRange
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
withElements:(NSArray *)elements
|
||||
{
|
||||
NSUInteger start = [self convertIndex:range.location
|
||||
NSUInteger start = [self convertIndex:aRange.location
|
||||
fromReferenceFrame:referenceFrame
|
||||
toReferenceFrame:MPSymbolReferenceFrame];
|
||||
NSUInteger end = [self convertIndex:NSMaxRange(range)
|
||||
NSUInteger end = [self convertIndex:NSMaxRange(aRange)
|
||||
fromReferenceFrame:referenceFrame
|
||||
toReferenceFrame:MPSymbolReferenceFrame];
|
||||
[self _replaceSymbolsInRange:NSMakeRange(start, end - start)
|
||||
withElements:elements];
|
||||
}
|
||||
|
||||
|
||||
- (void)_replaceSymbolsInRange:(NSRange)range
|
||||
withElements:(NSArray *)elements
|
||||
{
|
||||
@@ -439,8 +459,14 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
// Perform the replacement
|
||||
NSMutableArray *newElements = [[NSMutableArray alloc] initWithArray:elements
|
||||
copyItems:YES];
|
||||
NSArray *removedElements = [_elements subarrayWithRange:NSMakeRange(startIndex, endIndex-startIndex)];
|
||||
[_elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex)
|
||||
withObjectsFromArray:newElements];
|
||||
for (id<MPExpressionElement> element in removedElements) {
|
||||
if ([element isFunction]) {
|
||||
((MPFunction *)element).parent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
_tokenCache = nil;
|
||||
|
||||
@@ -469,6 +495,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
replacementLength:_replacementLength];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)_splitElementsAtLocation:(NSUInteger)location
|
||||
insertionIndex:(out NSUInteger *)insertionIndex
|
||||
{
|
||||
@@ -494,6 +521,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return splitOffset != 0;
|
||||
}
|
||||
|
||||
|
||||
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
@@ -501,6 +529,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
referenceFrame:referenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
@@ -508,12 +537,13 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
referenceFrame:referenceFrame];
|
||||
}
|
||||
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)range
|
||||
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)aRange
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
MPExpression *subexpression = [self copy];
|
||||
NSRange preceedingRange = NSMakeRange(0, range.location);
|
||||
NSUInteger firstOut = NSMaxRange(range);
|
||||
NSRange preceedingRange = NSMakeRange(0, aRange.location);
|
||||
NSUInteger firstOut = NSMaxRange(aRange);
|
||||
NSRange exceedingRange = NSMakeRange(firstOut, [self countItemsInReferenceFrame:referenceFrame] - firstOut);
|
||||
[subexpression deleteElementsInRange:exceedingRange
|
||||
referenceFrame:referenceFrame];
|
||||
@@ -522,8 +552,10 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return subexpression;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Evaluating Expressions
|
||||
|
||||
|
||||
- (NSDecimalNumber *)evaluateWithError:(NSError *__autoreleasing *)error
|
||||
{
|
||||
MPExpressionTree *tree = [self parse];
|
||||
@@ -537,6 +569,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return [[MPExpressionTree alloc] initWithTokenStream:tokenStream];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Notifications
|
||||
|
||||
|
||||
@@ -553,27 +586,6 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
#pragma mark Basic NSObject Methods
|
||||
|
||||
|
||||
/*
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
if (object == nil) {
|
||||
return NO;
|
||||
}
|
||||
if (![object isKindOfClass:[MPExpression class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [self isEqualToExpression:(MPExpression *)object];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToExpression:(MPExpression *)anExpression
|
||||
{
|
||||
return [self.elements isEqualToArray:anExpression.elements];
|
||||
}
|
||||
*/
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
#warning Bad Implementation
|
||||
@@ -608,6 +620,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return [_elements hash];
|
||||
@@ -633,6 +646,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
return [self initWithElements:[aDecoder decodeObject]];
|
||||
}
|
||||
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_elements];
|
||||
@@ -641,51 +655,60 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation MPExpression (MPExpressionConvenience)
|
||||
|
||||
#pragma mark Querying Expressions
|
||||
|
||||
|
||||
- (NSUInteger)countElements
|
||||
{
|
||||
return [self countItemsInReferenceFrame:MPElementReferenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)countSymbols
|
||||
{
|
||||
return [self countItemsInReferenceFrame:MPSymbolReferenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)countTokens
|
||||
{
|
||||
return [self countItemsInReferenceFrame:MPTokenReferenceFrame];
|
||||
}
|
||||
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index
|
||||
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex
|
||||
{
|
||||
return [self itemAtIndex:index
|
||||
return [self itemAtIndex:anIndex
|
||||
referenceFrame:MPElementReferenceFrame];
|
||||
}
|
||||
|
||||
- (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)index
|
||||
|
||||
- (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)anIndex
|
||||
{
|
||||
return [self itemAtIndex:index
|
||||
return [self itemAtIndex:anIndex
|
||||
referenceFrame:MPSymbolReferenceFrame];
|
||||
}
|
||||
|
||||
- (id<MPToken>)tokenAtIndex:(NSUInteger)index
|
||||
|
||||
- (id<MPToken>)tokenAtIndex:(NSUInteger)anIndex
|
||||
{
|
||||
return [self itemAtIndex:index
|
||||
return [self itemAtIndex:anIndex
|
||||
referenceFrame:MPTokenReferenceFrame];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Mutating Expressions
|
||||
|
||||
|
||||
- (void)appendElement:(id<MPExpressionElement>)anElement
|
||||
{
|
||||
[self appendElements:@[anElement]];
|
||||
}
|
||||
|
||||
|
||||
- (void)appendElements:(NSArray *)elements
|
||||
{
|
||||
[self replaceItemsInRange:NSMakeRange([self countItemsInReferenceFrame:MPSymbolReferenceFrame], 0)
|
||||
@@ -693,6 +716,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
withElements:elements];
|
||||
}
|
||||
|
||||
|
||||
- (void)insertElement:(id<MPExpressionElement>)anElement
|
||||
atIndex:(NSUInteger)index
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
@@ -702,6 +726,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
referenceFrame:referenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (void)insertElements:(NSArray *)elements
|
||||
atIndex:(NSUInteger)index
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
@@ -711,6 +736,7 @@ NSString *const MPExpressionPathErrorKey = @"MPExpressionPathErrorKey";
|
||||
withElements:elements];
|
||||
}
|
||||
|
||||
|
||||
- (void)deleteElementsInRange:(NSRange)range
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
|
||||
@@ -6,11 +6,52 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@protocol MPExpressionElement
|
||||
@brief This protocol defines the functionality an element in an
|
||||
expression must have.
|
||||
*/
|
||||
@protocol MPExpressionElement <NSObject, NSCopying, NSCoding>
|
||||
|
||||
|
||||
/*!
|
||||
@method isString
|
||||
@brief Returns wether the receiver is a string.
|
||||
|
||||
@discussion A string is defined by being an instance of @c NSString. If this
|
||||
method returns @c YES you can be sure it is an @c NSString
|
||||
instance.
|
||||
|
||||
@return @c YES if the receiver is a string, @c NO otherwise.
|
||||
*/
|
||||
- (BOOL)isString;
|
||||
|
||||
|
||||
/*!
|
||||
@method isFunction
|
||||
@brief Returns wether the receiver is a function.
|
||||
|
||||
@discussion A function is defined by being an instance of @c MPFunction. If
|
||||
this method returns @c YES you can be sure it is a @c MPFunction
|
||||
instance.
|
||||
|
||||
@return @c YES if the receiver is a function, @c NO otherwise.
|
||||
*/
|
||||
- (BOOL)isFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@method length
|
||||
@brief Calculates the length of the receiver.
|
||||
|
||||
@discussion The length of a @c MPExpressionElement is the number of symbols
|
||||
it consists of. For strings this is the number of characters in
|
||||
it. Functions have a length of @c 1.
|
||||
|
||||
@return The receiver's length.
|
||||
*/
|
||||
- (NSUInteger)length;
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,10 +6,32 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
@class MPExpressionTokenizer, MPExpression;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPExpressionTokenizer
|
||||
@brief The expression tokenizer class convers an @c MPExpression
|
||||
instance into an array of tokens.
|
||||
*/
|
||||
@interface MPExpressionTokenizer : NSObject
|
||||
|
||||
|
||||
/*!
|
||||
@method tokenizeExpression:
|
||||
@brief Converts an @c MPExpression instance into an array of tokens.
|
||||
|
||||
@discussion The objects in the returned array all conform to the @c MPToken
|
||||
protocol. Function tokens are not copied from the @c expression
|
||||
so they can still be mutated.
|
||||
|
||||
@param expression
|
||||
The expression to be tokenized.
|
||||
|
||||
@return An array of objects that conform to the @c MPToken protocol.
|
||||
*/
|
||||
+ (NSArray *)tokenizeExpression:(MPExpression *)expression; // Returns MPToken's
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
|
||||
#import "NSRegularExpression+MPParsingAdditions.h"
|
||||
|
||||
|
||||
#define MPRangeExists(range) (range.location != NSNotFound)
|
||||
|
||||
|
||||
|
||||
@implementation MPExpressionTokenizer
|
||||
|
||||
+ (NSArray *)tokenizeExpression:(MPExpression *)expression
|
||||
@@ -36,6 +39,8 @@
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+ (NSArray *)tokenizeElement:(NSString *)element
|
||||
elementSymbolIndex:(NSUInteger)symbolIndex
|
||||
{
|
||||
|
||||
@@ -8,13 +8,71 @@
|
||||
|
||||
#import "MPExpressionTreeElement.h"
|
||||
|
||||
|
||||
|
||||
@class MPExpressionTree;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPExpressionTree
|
||||
@brief The @c MPExpressionTree class is the main interface for working
|
||||
with the mathematical representation of an expression.
|
||||
|
||||
@discussion Expressions are represented as a tree structure of elements (all
|
||||
of which must conform to the @c MPExpressionTreeElement
|
||||
protocol). Most messages sent to instances of the @c
|
||||
MPExpressionTree class are cascaded down different parts of an
|
||||
expression in some way.
|
||||
*/
|
||||
@interface MPExpressionTree : NSObject <MPExpressionTreeElement>
|
||||
|
||||
/*!
|
||||
@property definedVariable
|
||||
@brief The variable this expression defines.
|
||||
|
||||
@discussion A variable definition must be at the beginning of an expression.
|
||||
If it defines a variable it must start with a single letter
|
||||
followed by an equals sign. The single letter is the name of the
|
||||
variable that is defined.
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *definedVariable;
|
||||
|
||||
|
||||
/*!
|
||||
@property summands
|
||||
@brief The summands that make up the receiver.
|
||||
|
||||
@discussion A expression mathematically can be interpreted as a series of
|
||||
summands. Summands are the different pars of an expression that
|
||||
are separated by + or - symbols. Every object in the returned
|
||||
array is guaranteed to conform to the @c MPExpressionTreeElement
|
||||
protocol.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSArray *summands;
|
||||
|
||||
|
||||
/*!
|
||||
@method validateExpectingVariableDefinition:error:
|
||||
@brief Validates the receiver.
|
||||
|
||||
@discussion Using this method you can validate an expression that contains a
|
||||
variable definition. If a variable definition is expected but not
|
||||
found this method returns @c NO. Likewise @c NO is returned if
|
||||
you do not expect a variable definition but one is found.
|
||||
|
||||
@param flag
|
||||
Specifies wether or not to expect a variable definition at the
|
||||
beginning of the expression.
|
||||
|
||||
@param error
|
||||
If there is a syntax error in the receiver this parameter will be
|
||||
set to an appropriate value. If you are not interested in the
|
||||
type of syntax error pass @c NULL.
|
||||
|
||||
@return @c YES if the receiver is valid, @c NO otherwise. If @c NO is
|
||||
returned the @c error parameter should be set to an appropriate
|
||||
value.
|
||||
*/
|
||||
- (BOOL)validateExpectingVariableDefinition:(BOOL)flag
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#import "MPExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPExpressionTree {
|
||||
NSMutableArray *_summands;
|
||||
}
|
||||
@@ -28,6 +30,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
|
||||
{
|
||||
self = [self init];
|
||||
@@ -51,17 +54,20 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)summands
|
||||
{
|
||||
return _summands;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)validate:(NSError *__autoreleasing *)error
|
||||
{
|
||||
return [self validateExpectingVariableDefinition:NO
|
||||
error:error];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)validateExpectingVariableDefinition:(BOOL)flag
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
@@ -100,6 +106,7 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (NSDecimalNumber *)evaluate
|
||||
{
|
||||
NSDecimalNumber *value = [NSDecimalNumber zero];
|
||||
|
||||
@@ -5,16 +5,85 @@
|
||||
// Created by Kim Wittenburg on 09.10.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
// TODO: Replace internal inconsistency exception with something else
|
||||
|
||||
|
||||
|
||||
@class MPTokenStream;
|
||||
|
||||
|
||||
/*!
|
||||
@protocol MPExpressionTreeElement
|
||||
@brief This protocol defines the methods that are essential for dealing
|
||||
with an expression in a mathematical context.
|
||||
|
||||
@discussion Dealing with an expression in a mathematical context involves
|
||||
anything from evaluation to transformation of expressions and
|
||||
equations.
|
||||
*/
|
||||
@protocol MPExpressionTreeElement <NSObject>
|
||||
@required
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
/*!
|
||||
@method initWithTokenStream:
|
||||
@brief Initializes the expression tree element from the given token
|
||||
strem.
|
||||
|
||||
@discussion This method consumes the tokens that make up this element. If the
|
||||
token stream does not start with a token that is appropriate for
|
||||
the initializied expression tree element a @c
|
||||
NSInternalInconsistency exception is raised.
|
||||
|
||||
@param tokenStream
|
||||
The token stream the receiver is to be initialized from.
|
||||
|
||||
@return A new instance.
|
||||
*/
|
||||
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream;
|
||||
|
||||
|
||||
/*!
|
||||
@method validate:
|
||||
@brief Validates the receiver.
|
||||
|
||||
@discussion Validation should only check for syntax errors. Mathematical
|
||||
errors like a division by @c 0 should be handled with in @c
|
||||
-evaluate.
|
||||
|
||||
@param error
|
||||
If there is a syntax error in the receiver this parameter should
|
||||
be set to an appropriate value. If you are not interested in the
|
||||
type of syntax error pass @c NULL.
|
||||
|
||||
@return @c YES if the receiver is valid, @c NO otherwise. If @c NO is
|
||||
returned the @c error parameter should be set to an appropriate
|
||||
value.
|
||||
*/
|
||||
- (BOOL)validate:(NSError *__autoreleasing *)error;
|
||||
- (NSDecimalNumber *)evaluate;
|
||||
|
||||
|
||||
/*!
|
||||
@method evaluate:
|
||||
@brief Evaluates the receiver.
|
||||
|
||||
@discussion Evaluation does not take syntax errors into account. Before this
|
||||
method is called you must call @c validate: to make sure that the
|
||||
reciever is valid for evaluation. The result of evaluation with
|
||||
@c -validate: returning @c NO may be unexpected or evaluation may
|
||||
completely fail.
|
||||
|
||||
@param error
|
||||
If any errors occur during evaluation (such as division by zero)
|
||||
this parameter will be set to an appropriate value. If you are
|
||||
not interested in errors pass @c NULL.
|
||||
@warning This method is not responsible for syntax validation. Use @c
|
||||
-validate: instead.
|
||||
|
||||
@return The result of the evaluationa or @c nil or @c NaN if evaluation
|
||||
fails. If evaluation fails the @c error parameter will be set to
|
||||
an apporpriate value.
|
||||
*/
|
||||
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
#import "MPFunction.h"
|
||||
#import "MPToken.h"
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category MPFunction (MPToken)
|
||||
@brief This category adds @c MPToken protocol conformance to the @c
|
||||
MPFunction class.
|
||||
*/
|
||||
@interface MPFunction (MPToken) <MPToken>
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
return MPGenericFunctionToken;
|
||||
}
|
||||
|
||||
|
||||
- (NSRange)range
|
||||
{
|
||||
NSUInteger selfIndex = [self.parent indexOfElement:self];
|
||||
@@ -26,11 +27,13 @@
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)exists
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)stringValue
|
||||
{
|
||||
return [self description];
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
#import "MPExpressionElement.h"
|
||||
|
||||
|
||||
|
||||
/* Use this macro to implement a custom setter for children of custom MPFunction
|
||||
* subclasses. This 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.
|
||||
*/
|
||||
#define MPFunctionAccessorImplementation(Accessor, variableName) \
|
||||
- (void)set##Accessor:(MPExpression *)value \
|
||||
{ \
|
||||
@@ -17,51 +24,297 @@
|
||||
[self didChangeChildAtIndex:[self indexOfChild:variableName]]; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
@class MPFunction, MPExpression, MPRangePath;
|
||||
@protocol MPExpressionElement;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPFunction
|
||||
@brief The @c MPFunction class represents a mathematical function.
|
||||
|
||||
@discussion A mathematical function can be anything that exceeds the
|
||||
graphical capability of text.
|
||||
*/
|
||||
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
#pragma mark Properties
|
||||
// Subclasses should define accessor properties for all sub expressions, which, in the setter, should send didChangeElementAtRangePath:replacementLength: to self with a replacement length of 1.
|
||||
/* Subclasses should define readwrite properties for all child expressions they
|
||||
* can have. In the implementation file the property setter should be overridden
|
||||
* using the @c MPFunctionAccessorImplementation macro. The macro takes two
|
||||
* parameters: The capitalized name of the property and the name of the
|
||||
* property's backing variable (usually the property's name prefixed with an
|
||||
* underscore.
|
||||
*
|
||||
* Note that none of the children may be nil at any time.
|
||||
*/
|
||||
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
@property (nonatomic, weak) MPExpression *parent; // Documentation: Do not set
|
||||
/*!
|
||||
@methodgroup Working With the Expression Tree
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@property parent
|
||||
@brief 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 an expression.
|
||||
@note If you need to know when a function is added to or removed from
|
||||
an expression you can observe this property.
|
||||
|
||||
@return The parent of the receiver or @c nil if the receiver does not
|
||||
have a parent.
|
||||
*/
|
||||
@property (nonatomic, weak) MPExpression *parent;
|
||||
|
||||
|
||||
/*!
|
||||
@method rootExpression
|
||||
@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. A root expression does not have
|
||||
a parent.
|
||||
|
||||
@return The root expression from the receiver's expression tree or @c nil
|
||||
if this function does not have a parent.
|
||||
*/
|
||||
- (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 element 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 or @c nil
|
||||
if this function does not have a parent.
|
||||
*/
|
||||
- (NSIndexPath *)indexPath;
|
||||
|
||||
- (NSArray *)childrenAccessors; // Override
|
||||
|
||||
/*!
|
||||
@method numberOfChildren
|
||||
@brief Returns the number of children the receiver has.
|
||||
|
||||
@discussion The number of children must be equal to the number of child
|
||||
accessors as determined by the @c -childrenAccessors method.
|
||||
|
||||
@return The number of children of the receiving function.
|
||||
*/
|
||||
- (NSUInteger)numberOfChildren;
|
||||
|
||||
|
||||
/*!
|
||||
@method childAtIndex:
|
||||
@brief Returns the child at the given index.
|
||||
|
||||
@discussion The ordering of children is determined by the order of the
|
||||
children returned from the @c -childrenAccessors method.
|
||||
|
||||
@param index
|
||||
The index of the requested child.
|
||||
|
||||
@return The expression that corresponds to the child at @c index.
|
||||
*/
|
||||
- (MPExpression *)childAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method setChild:atIndex:
|
||||
@brief Sets the child at the specified index.
|
||||
|
||||
@discussion Although this method does @b not call the respective accessor
|
||||
methods it behaves exactly as the setter method implemented using
|
||||
the @c MPFunctionAccessorImplementation 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;
|
||||
|
||||
- (NSArray *)children; // Indexes must equal the ones from the other methods
|
||||
|
||||
/*!
|
||||
@method children
|
||||
@brief Returns the receiver's children.
|
||||
|
||||
@discussion The ordering of the children is the same as in the array returned
|
||||
from @c -childrenAccessors.
|
||||
|
||||
@return An array containing all the function's children.
|
||||
*/
|
||||
- (NSArray *)children;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfChild:
|
||||
@brief Returns the index of @c child in the receiver.
|
||||
|
||||
@param child
|
||||
The child to be searched.
|
||||
|
||||
@return The index of @c child or @c NSNotFound if none of the receiver's
|
||||
children is equal to @c child.
|
||||
*/
|
||||
- (NSUInteger)indexOfChild:(MPExpression *)child;
|
||||
|
||||
|
||||
/*!
|
||||
@method elementAtIndexPath:
|
||||
@brief Returns the element at the specified index path.
|
||||
|
||||
@discussion This method @em walks down the expression tree (including other
|
||||
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 indexes are
|
||||
expressed in the element 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
|
||||
made to the returned object are reflected in the receiver.
|
||||
*/
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
|
||||
- (BOOL)validate:(NSError *__autoreleasing *)error; // Override
|
||||
- (NSDecimalNumber *)evaluate; // Override
|
||||
#pragma mark Notifications
|
||||
/*!
|
||||
@methodgroup Notifications
|
||||
*/
|
||||
|
||||
#pragma mark Messages
|
||||
|
||||
/*!
|
||||
@method didChangeElementsInRangePath:replacementLength:
|
||||
@brief Notification method that is called if one of the children was
|
||||
mutated.
|
||||
|
||||
@discussion This method is also 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:
|
||||
@brief Convenience method that calls @c
|
||||
-didChangeElementsInRangePath:replacementLength:
|
||||
|
||||
@discussion This method is automatically called when one of the children is
|
||||
changed using one of the accessor methods or @c
|
||||
-setChild:atIndex:.
|
||||
|
||||
@param index
|
||||
The index of the child that was changed.
|
||||
*/
|
||||
- (void)didChangeChildAtIndex:(NSUInteger)index;
|
||||
|
||||
#pragma mark Working With Functions
|
||||
// - (BOOL)isEqualToFunction:(MPFunction *)aFunction; // Override
|
||||
@end
|
||||
|
||||
- (NSString *)description; // Should be overridden
|
||||
- (NSUInteger)hash;
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone;
|
||||
|
||||
/*!
|
||||
@category MPFunction (MPSubcalssOverride)
|
||||
@brief The methods in this category must be implemented by any concrete
|
||||
subclasses of @c MPFunction.
|
||||
|
||||
@discussion @c 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
|
||||
@brief Returns the names of the children properties.
|
||||
|
||||
@discussion A @c MPFunction subclass should declare a public propertiy for
|
||||
every child it can have. This method should return the names as
|
||||
they would be used for key-value-coding.
|
||||
|
||||
@return An array of strings describing the names of children a function
|
||||
has.
|
||||
*/
|
||||
- (NSArray *)childrenAccessors;
|
||||
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
/*!
|
||||
@methodgroup Evaluating Functions
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method validate:
|
||||
@brief Validates the function and all its children for syntax errors.
|
||||
|
||||
@param error
|
||||
If the validation fails this parameter should be set to an
|
||||
appropriate value.
|
||||
|
||||
@return @c YES if the validation was successful, @c NO otherwise.
|
||||
*/
|
||||
- (BOOL)validate:(NSError *__autoreleasing *)error;
|
||||
|
||||
|
||||
/*!
|
||||
@method evaluate:
|
||||
@brief Evaluates the function.
|
||||
|
||||
@discussion Function evaluation should actually perform math. Also the
|
||||
implementation of this method assumes that the function and all
|
||||
children are valid (as determined by the @c -validate: method).
|
||||
|
||||
@param error
|
||||
If a mathematical error occurs it is reflected in this parameter.
|
||||
Depending on the error this method then either returns @c nil or
|
||||
@c NaN.
|
||||
|
||||
@return The result of the evaluation or @c nil or @c NaN if an evaluation
|
||||
error occured (such as a division by @c 0).
|
||||
*/
|
||||
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error;
|
||||
|
||||
/* In Addition to the above methods MPFunction subclasses should also override
|
||||
* the NSObject methods -description and -hash.
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,9 +12,13 @@
|
||||
#import "MPRangePath.h"
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPFunction
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
@@ -29,35 +33,49 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
|
||||
|
||||
- (MPExpression *)rootExpression
|
||||
{
|
||||
return [self.parent rootExpression];
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPath
|
||||
{
|
||||
if (!self.parent) {
|
||||
return nil;
|
||||
}
|
||||
NSUInteger selfIndex = [self.parent indexOfElement:self];
|
||||
return [[self.parent indexPath] indexPathByAddingIndex:selfIndex];
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)numberOfChildren
|
||||
{
|
||||
return self.childrenAccessors.count;
|
||||
}
|
||||
|
||||
|
||||
- (MPExpression *)childAtIndex:(NSUInteger)index
|
||||
{
|
||||
return [self valueForKey:self.childrenAccessors[index]];
|
||||
}
|
||||
|
||||
|
||||
- (void)setChild:(MPExpression *)child
|
||||
atIndex:(NSUInteger)index
|
||||
{
|
||||
[self setValue:child forKey:self.childrenAccessors[index]];
|
||||
[[self valueForKey:self.childrenAccessors[index]] setParent:nil];
|
||||
[self setValue:child
|
||||
forKey:self.childrenAccessors[index]];
|
||||
child.parent = self;
|
||||
[self didChangeChildAtIndex:index];
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
NSUInteger childCount = [self numberOfChildren];
|
||||
@@ -68,8 +86,10 @@
|
||||
return [children copy];
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)indexOfChild:(MPExpression *)child
|
||||
{
|
||||
|
||||
NSUInteger index = 0;
|
||||
for (; index < [self numberOfChildren]; index++) {
|
||||
if ([self childAtIndex:index] == child) {
|
||||
@@ -79,6 +99,7 @@
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.length == 0) {
|
||||
@@ -88,7 +109,10 @@
|
||||
return [child elementAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Notifications
|
||||
|
||||
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength
|
||||
{
|
||||
@@ -98,6 +122,7 @@
|
||||
replacementLength:replacementLength];
|
||||
}
|
||||
|
||||
|
||||
- (void)didChangeChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
MPRangePath *path = [[MPRangePath alloc] initWithRange:NSMakeRange(index, 1)];
|
||||
@@ -105,39 +130,26 @@
|
||||
replacementLength:1];
|
||||
}
|
||||
|
||||
#pragma mark Working With Functions
|
||||
/*
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
if (object == nil) {
|
||||
return NO;
|
||||
}
|
||||
if (![object isKindOfClass:[MPFunction class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [self isEqualToFunction:(MPFunction *)object];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
|
||||
{
|
||||
return [aFunction isMemberOfClass:[MPFunction class]] && [self isMemberOfClass:[MPFunction class]];
|
||||
}*/
|
||||
#pragma mark Working With Functions
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return @"[]";
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
#warning Unimplemented Method
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
id copy = [[self.class allocWithZone:zone] init];
|
||||
@@ -148,7 +160,10 @@
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super init];
|
||||
@@ -163,22 +178,28 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:self.children];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - MPExpressionElement
|
||||
|
||||
|
||||
- (BOOL)isString
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isFunction
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)length
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
if (self) {
|
||||
[tokenStream beginIgnoringWhitespaceTokens];
|
||||
|
||||
while (tokenStream.currentToken.tokenType != MPOperatorListToken && tokenStream.currentToken.tokenType != MPEOFToken) {
|
||||
while ([tokenStream hasMoreTokens] && tokenStream.currentToken.tokenType != MPOperatorListToken) {
|
||||
[_factors addObject:[[MPFactor alloc] initWithTokenStream:tokenStream]];
|
||||
}
|
||||
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
#import "MPExpression.h"
|
||||
|
||||
/// Convenience Constructor Macro
|
||||
#define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)]
|
||||
|
||||
|
||||
@class MPRangePath, MPExpression;
|
||||
|
||||
|
||||
@@ -27,7 +29,11 @@
|
||||
*/
|
||||
@interface MPRangePath : NSObject <NSCopying, NSCoding>
|
||||
|
||||
|
||||
#pragma mark Creation Methods
|
||||
/*!
|
||||
@methodgroup Creation Methods
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@@ -80,7 +86,7 @@
|
||||
|
||||
|
||||
/*!
|
||||
@method emptyRangePath
|
||||
@method rangePath
|
||||
@brief Allocates and initializes an empty @c NSRangePath instance.
|
||||
|
||||
@discussion An empty range path's location has @c 0 indexes and a length of
|
||||
@@ -88,7 +94,23 @@
|
||||
|
||||
@return A newly created @c MPRangePath instance.
|
||||
*/
|
||||
+ (instancetype)emptyRangePath;
|
||||
+ (instancetype)rangePath;
|
||||
|
||||
|
||||
/*!
|
||||
@method rangePathWithRange:
|
||||
@brief Allocates a new @c MPRangePath instance and initializes it with
|
||||
the given location and length.
|
||||
|
||||
@discussion The location of @c aRange is translated into an index path for
|
||||
the range path.
|
||||
|
||||
@param aRange
|
||||
The range to be converted into a range path.
|
||||
|
||||
@return A newly created @c MPRangePath instance.
|
||||
*/
|
||||
+ (instancetype)rangePathWithRange:(NSRange)aRange;
|
||||
|
||||
|
||||
/*!
|
||||
@@ -110,22 +132,10 @@
|
||||
length:(NSUInteger)length;
|
||||
|
||||
|
||||
/*!
|
||||
@method rangePathWithRange:
|
||||
@brief Allocates a new @c MPRangePath instance and initializes it with
|
||||
the given location and length.
|
||||
|
||||
@discussion The location of @c aRange is translated into an index path for
|
||||
the range path.
|
||||
|
||||
@param aRange
|
||||
The range to be converted into a range path.
|
||||
|
||||
@return A newly created @c MPRangePath instance.
|
||||
*/
|
||||
+ (instancetype)rangePathWithRange:(NSRange)aRange;
|
||||
|
||||
#pragma mark Properties
|
||||
/*!
|
||||
@methodgroup Properties
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@@ -165,26 +175,11 @@
|
||||
*/
|
||||
- (NSRange)rangeAtLastIndex;
|
||||
|
||||
#pragma mark Working with Ranges
|
||||
|
||||
|
||||
#pragma mark Working With Ranges
|
||||
/*!
|
||||
@method containsRangePath:
|
||||
@brief Checks wether the receiver completely contains @c aRangePath.
|
||||
|
||||
@discussion A range path is contained by another range path if either the
|
||||
receiver's range at its last index contains the index at the
|
||||
respective location of the location path of @c aRangePath or (if
|
||||
the locations are of the same length) the receiver's range at its
|
||||
last index contains the last index's range of @c aRangePath.
|
||||
|
||||
@param aRangePath
|
||||
The range path to check wether it is contained in the receiver.
|
||||
|
||||
@return @c YES if the range path addressed by the receiver also includes
|
||||
@c aRangePath, @c NO otherwise.
|
||||
@methodgroup Working With Ranges
|
||||
*/
|
||||
- (BOOL)containsRangePath:(MPRangePath *)aRangePath;
|
||||
|
||||
|
||||
/*!
|
||||
@@ -207,7 +202,23 @@
|
||||
- (BOOL)containsLocation:(NSIndexPath *)location;
|
||||
|
||||
|
||||
- (BOOL)isEqual:(id)object;
|
||||
/*!
|
||||
@method containsRangePath:
|
||||
@brief Checks wether the receiver completely contains @c aRangePath.
|
||||
|
||||
@discussion A range path is contained by another range path if either the
|
||||
receiver's range at its last index contains the index at the
|
||||
respective location of the location path of @c aRangePath or (if
|
||||
the locations are of the same length) the receiver's range at its
|
||||
last index contains the last index's range of @c aRangePath.
|
||||
|
||||
@param aRangePath
|
||||
The range path to check wether it is contained in the receiver.
|
||||
|
||||
@return @c YES if the range path addressed by the receiver also includes
|
||||
@c aRangePath, @c NO otherwise.
|
||||
*/
|
||||
- (BOOL)containsRangePath:(MPRangePath *)aRangePath;
|
||||
|
||||
|
||||
/*!
|
||||
@@ -229,6 +240,8 @@
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface MPExpression (MPRangeExtension)
|
||||
|
||||
|
||||
@@ -252,6 +265,27 @@
|
||||
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame;
|
||||
|
||||
|
||||
/*!
|
||||
@method replaceItemsInRangePath:referenceFrame:withElements:
|
||||
@brief Replaces the items in the specified range path with the contents
|
||||
of @c elements.
|
||||
|
||||
@discussion All objects in the @c elements array must conform to the @c
|
||||
MPExpressionElement protocol. If one or more objects do not
|
||||
conform to the protocol a @c MPIllegalElementException will be
|
||||
raised.
|
||||
|
||||
@param rangePath
|
||||
The range path to be replaced. The path of the range path is
|
||||
expressed in the element reference frame. The range of the range
|
||||
path is expressed in the specified @c referenceFrame.
|
||||
|
||||
@param referenceFrame
|
||||
The reference frame to use. This only applies to the range at the
|
||||
last index of the range path. The path of the range path is
|
||||
always expressed in the element reference frame.
|
||||
*/
|
||||
- (void)replaceItemsInRangePath:(MPRangePath *)rangePath
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
withElements:(NSArray *)elements;
|
||||
|
||||
@@ -8,16 +8,21 @@
|
||||
|
||||
#import "MPRangePath.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPRangePath
|
||||
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithLocation:[[NSIndexPath alloc] init]
|
||||
length:0];
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithLocation:(NSIndexPath *)location
|
||||
length:(NSUInteger)length
|
||||
{
|
||||
@@ -29,17 +34,26 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithRange:(NSRange)aRange
|
||||
{
|
||||
return [self initWithLocation:[[NSIndexPath alloc] initWithIndex:aRange.location]
|
||||
length:aRange.length];
|
||||
}
|
||||
|
||||
+ (instancetype)emptyRangePath
|
||||
|
||||
+ (instancetype)rangePath
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype)rangePathWithRange:(NSRange)aRange
|
||||
{
|
||||
return [[self alloc] initWithRange:aRange];
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype)rangePathWithLocation:(NSIndexPath *)location
|
||||
length:(NSUInteger)length
|
||||
{
|
||||
@@ -47,13 +61,10 @@
|
||||
length:length];
|
||||
}
|
||||
|
||||
+ (instancetype)rangePathWithRange:(NSRange)aRange
|
||||
{
|
||||
return [[self alloc] initWithRange:aRange];
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
|
||||
- (NSIndexPath *)maxRangePath
|
||||
{
|
||||
NSUInteger lastIndex = [self.location indexAtPosition:self.location.length-1];
|
||||
@@ -61,12 +72,21 @@
|
||||
return [[self.location indexPathByRemovingLastIndex] indexPathByAddingIndex:newLastIndex];
|
||||
}
|
||||
|
||||
|
||||
- (NSRange)rangeAtLastIndex
|
||||
{
|
||||
return NSMakeRange([self.location indexAtPosition:self.location.length-1], self.length);
|
||||
}
|
||||
|
||||
#pragma mark Working with Ranges
|
||||
|
||||
#pragma mark Working With Ranges
|
||||
|
||||
|
||||
- (BOOL)containsLocation:(NSIndexPath *)location
|
||||
{
|
||||
return [self containsRangePath:[[MPRangePath alloc] initWithLocation:location length:0]];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)containsRangePath:(MPRangePath *)aRangePath
|
||||
{
|
||||
@@ -91,10 +111,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)containsLocation:(NSIndexPath *)location
|
||||
{
|
||||
return [self containsRangePath:[[MPRangePath alloc] initWithLocation:location length:0]];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
@@ -110,11 +126,13 @@
|
||||
return [self isEqualToRangePath:(MPRangePath *)object];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isEqualToRangePath:(MPRangePath *)aRangePath
|
||||
{
|
||||
return [self.location isEqual:aRangePath.location] && self.length == aRangePath.length;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSMutableString *description = [[NSMutableString alloc] initWithString:@"MPRangePath<location="];
|
||||
@@ -129,30 +147,38 @@
|
||||
return description.copy;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
MPRangePath *copy = [[MPRangePath allocWithZone:zone] initWithLocation:self.location.copy length:self.length];
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
return [self initWithLocation:[aDecoder decodeObjectForKey:@"location"]
|
||||
length:[aDecoder decodeIntegerForKey:@"length"]];
|
||||
}
|
||||
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:self.location forKey:@"location"];
|
||||
[aCoder encodeInteger:self.length forKey:@"length"];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation MPExpression (MPRangeExtension)
|
||||
|
||||
- (NSArray *)itemsInRangePath:(MPRangePath *)rangePath
|
||||
@@ -162,6 +188,7 @@
|
||||
return [targetExpression itemsInRange:rangePath.rangeAtLastIndex referenceFrame:referenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
{
|
||||
@@ -174,6 +201,7 @@
|
||||
referenceFrame:referenceFrame];
|
||||
}
|
||||
|
||||
|
||||
- (void)replaceItemsInRangePath:(MPRangePath *)rangePath
|
||||
referenceFrame:(MPReferenceFrame)referenceFrame
|
||||
withElements:(NSArray *)elements
|
||||
|
||||
@@ -8,11 +8,37 @@
|
||||
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
|
||||
@class MPSuffixFunction;
|
||||
@protocol MPValue;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPSuffixFunction
|
||||
@brief This is the common superclass for all functions that apply to the
|
||||
value preceeding them.
|
||||
|
||||
@discussion One example for a suffix function is a power. Powers apply to a
|
||||
base value and are evaluated with very high priority. In fact the
|
||||
base value may not be used without previously having evaluated
|
||||
the suffix function.
|
||||
|
||||
Another special thing about suffix functions is that they need to
|
||||
know more than their own children (namely the base value) to be
|
||||
evaluated. To be able to do this suffix functions have a special
|
||||
property @c baseValue that is set before the function is
|
||||
evaluated.
|
||||
*/
|
||||
@interface MPSuffixFunction : MPFunction
|
||||
|
||||
/*!
|
||||
@property baseValue
|
||||
@brief The receiver's base value.
|
||||
|
||||
@discussion The base value is the thing a suffix function applies to (e.g.
|
||||
a power's base).
|
||||
*/
|
||||
@property (nonatomic, strong) id<MPValue> baseValue;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#import "MPSuffixFunction.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPSuffixFunction
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,12 +8,54 @@
|
||||
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
|
||||
@class MPSumFunction, MPExpression;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPSumFunction
|
||||
@brief A sum function (generally noted using a capital sigma) evaluates
|
||||
a term repeatedly and sums up the results.
|
||||
|
||||
@discussion A sum function has a start and a target expression indicating how
|
||||
often the sum expression should be evaluated. Both the value of
|
||||
the start expression and the target expressions are included in
|
||||
the iterations.
|
||||
|
||||
Each iteration the sum value is incremented by @c 1. If the start
|
||||
and target expression evaluate to the same value the sum is
|
||||
evaluated once.
|
||||
*/
|
||||
@interface MPSumFunction : MPFunction
|
||||
|
||||
@property (nonatomic, strong) MPExpression *startExpression; // Index 0
|
||||
@property (nonatomic, strong) MPExpression *targetExpression; // Index 1
|
||||
@property (nonatomic, strong) MPExpression *sumExpression; // Index 2
|
||||
|
||||
/*!
|
||||
@property startExpression
|
||||
@brief The value of the first iteration.
|
||||
|
||||
@discussion The start expression must define a variable that may be used in
|
||||
the sum expression. If the start expression does not define a
|
||||
variable the sum function will fail on validation.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *startExpression; /* Index 0 */
|
||||
|
||||
|
||||
/*!
|
||||
@property startExpression
|
||||
@brief The value if the last iteration.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *targetExpression; /* Index 1 */
|
||||
|
||||
|
||||
/*!
|
||||
@property sumExpression
|
||||
@brief The sum expression evaluated multiple times.
|
||||
|
||||
@discussion During evaluation of the sum expression the variable defined in
|
||||
the start expression is available. That variable always contains
|
||||
the value of the current iteration.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpression *sumExpression; /* Index 2 */
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,19 +12,25 @@
|
||||
#import "MPExpressionTree.h"
|
||||
#import "MPEvaluationContext.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPSumFunction
|
||||
|
||||
|
||||
MPFunctionAccessorImplementation(StartExpression, _startExpression)
|
||||
MPFunctionAccessorImplementation(TargetExpression, _targetExpression)
|
||||
MPFunctionAccessorImplementation(SumExpression, _sumExpression)
|
||||
|
||||
|
||||
- (NSArray *)childrenAccessors
|
||||
{
|
||||
return @[@"startExpression", @"targetExpression", @"sumExpression"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
|
||||
|
||||
- (BOOL)validate:(NSError *__autoreleasing *)error
|
||||
{
|
||||
MPExpressionTree *startTree = [self.startExpression parse];
|
||||
@@ -47,6 +53,7 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (NSDecimalNumber *)evaluate
|
||||
{
|
||||
MPExpressionTree *startTree = [self.startExpression parse];
|
||||
@@ -67,8 +74,10 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Working With Functions
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Sum(From: %@; To: %@; Using: %@)", self.startExpression, self.targetExpression, self.sumExpression];
|
||||
|
||||
@@ -8,11 +8,35 @@
|
||||
|
||||
#import "MPExpressionTreeElement.h"
|
||||
|
||||
|
||||
|
||||
@class MPSummand, MPOperatorChain, MPProduct;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPSummand
|
||||
@brief A summand is a part of an expression that consists of a list of
|
||||
addition and subtraction operators and a product.
|
||||
*/
|
||||
@interface MPSummand : NSObject <MPExpressionTreeElement>
|
||||
|
||||
|
||||
/*!
|
||||
@property operatorChain
|
||||
@brief The summand's preceeding operators.
|
||||
|
||||
@discussion The operator chain is interpreted as a factor for the @c product
|
||||
property of the summand during evaluation.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) MPOperatorChain *operatorChain;
|
||||
|
||||
|
||||
/*!
|
||||
@property product
|
||||
@brief The summand's product.
|
||||
|
||||
@discussion The product is the @em value of a summand.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) MPProduct *product;
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,11 +6,66 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
@class MPToken;
|
||||
@protocol MPToken;
|
||||
|
||||
|
||||
/*!
|
||||
@typedef MPTokenType
|
||||
@brief A type of a token identifies its behaviour in a mathematical
|
||||
context.
|
||||
|
||||
@constant MPMultiplicationSymbolToken
|
||||
A multiplication symbol.
|
||||
|
||||
@constant MPOperatorListToken
|
||||
A list of operators (+ and - symbols). The token may be longer
|
||||
than the number of operators if there are spaces between them.
|
||||
|
||||
@constant MPSinToken
|
||||
The sin function.
|
||||
|
||||
@constant MPCosToken
|
||||
The cos function.
|
||||
|
||||
@constant MPTanToken
|
||||
The tan function.
|
||||
|
||||
@constant MPASinToken
|
||||
The asin function.
|
||||
|
||||
@constant MPACosToken
|
||||
The acos function.
|
||||
|
||||
@constant MPATanToken
|
||||
The atan function.
|
||||
|
||||
@constant MPNumberToken
|
||||
A number. This may be an integer or a floating point number.
|
||||
Floating point numbers contain a @c NSLocaleDecimalSeparator.
|
||||
|
||||
@constant MPVariableToken
|
||||
A variable. A variable is exactly one character long.
|
||||
|
||||
@constant MPFactorialToken
|
||||
The factorial symbol (!).
|
||||
|
||||
@constant MPEqualsToken
|
||||
The equals sign.
|
||||
|
||||
@constant MPGenericFunctionToken
|
||||
A function represented by the @c MPFunction class. A token with
|
||||
this token type is guaranteed to be a @c MPFunction instance.
|
||||
|
||||
@constant MPWhitespaceToken
|
||||
A whitespace. This token can typically be ignored.
|
||||
|
||||
@constant MPUnidentifiedToken
|
||||
Any symbol that does not match any other token.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, MPTokenType) {
|
||||
MPEOFToken = 0,
|
||||
MPMultiplicationSymbolToken,
|
||||
MPOperatorListToken,
|
||||
MPSinToken,
|
||||
@@ -29,21 +84,85 @@ typedef NS_ENUM(NSUInteger, MPTokenType) {
|
||||
MPUnidentifiedToken,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@protocol MPToken
|
||||
@brief Tokens represent logical units in an expresion.
|
||||
*/
|
||||
@protocol MPToken <NSObject>
|
||||
|
||||
|
||||
/*!
|
||||
@method tokenType
|
||||
@brief Returns the receiver's token type.
|
||||
|
||||
@discussion The token type identifies what kind of token the receiver is. For
|
||||
more information see the documentation on the @c MPTokenType
|
||||
enum.
|
||||
|
||||
@return The receiver's token type.
|
||||
*/
|
||||
- (MPTokenType)tokenType;
|
||||
|
||||
|
||||
/*!
|
||||
@method range
|
||||
@brief Returns the receiver's range.
|
||||
|
||||
@discussion The range identifies where the token is in the expression. It is
|
||||
specified in the symbol reference frame.
|
||||
|
||||
@return The range the token occupies in the expression it was parsed
|
||||
from specified in the symbol reference frame.
|
||||
*/
|
||||
- (NSRange)range;
|
||||
|
||||
|
||||
/*!
|
||||
@method stringValue
|
||||
@brief The string that caused the token to be parsed.
|
||||
|
||||
@discussion Depending on the type of the token the string value can have a
|
||||
fixed or variable length. For example the equals token always has
|
||||
a length of @c 1 whereas a number or whitespace token can be much
|
||||
longer.
|
||||
|
||||
@return The receiver's string value.
|
||||
*/
|
||||
- (NSString *)stringValue;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@class MPToken
|
||||
@brief The @c MPToken class implements the functionality of the @c
|
||||
MPToken protocol. Most tokens are instances of the @c MPToken
|
||||
class.
|
||||
*/
|
||||
@interface MPToken : NSObject <MPToken>
|
||||
|
||||
- (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation;
|
||||
|
||||
/*!
|
||||
@method initWithTokenType:range:stringValue:
|
||||
@brief Creates a new @c MPToken instance.
|
||||
|
||||
@param tokenType
|
||||
The type of the token.
|
||||
|
||||
@param range
|
||||
The range of the token in the expression. Specified in the symbol
|
||||
reference frame.
|
||||
|
||||
@param input
|
||||
The string value of the token.
|
||||
|
||||
@return A newly initialized token.
|
||||
*/
|
||||
- (instancetype)initWithTokenType:(MPTokenType)tokenType
|
||||
range:(NSRange)range
|
||||
stringValue:(NSString *)input;
|
||||
stringValue:(NSString *)input; /* designated initializer */
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,21 +8,14 @@
|
||||
|
||||
#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)initWithTokenType:(MPTokenType)tokenType
|
||||
range:(NSRange)range
|
||||
stringValue:(NSString *)input
|
||||
@@ -36,21 +29,25 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (NSRange)range
|
||||
{
|
||||
return _range;
|
||||
}
|
||||
|
||||
|
||||
- (MPTokenType)tokenType
|
||||
{
|
||||
return _tokenType;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)stringValue
|
||||
{
|
||||
return _stringValue;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return self.stringValue;
|
||||
|
||||
@@ -6,23 +6,132 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
@class MPTokenStream;
|
||||
@protocol MPToken;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPTokenStream
|
||||
@brief This class is a helper class for processing tokens from the
|
||||
beginning of an expression to the end.
|
||||
|
||||
@discussion A token stream is a single-use object. After the stream has
|
||||
reached the end there is no way to reset it.
|
||||
*/
|
||||
@interface MPTokenStream : NSObject
|
||||
|
||||
- (instancetype)initWithTokens:(NSArray *)tokens;
|
||||
|
||||
@property (nonatomic, copy) NSArray *tokens;
|
||||
/*!
|
||||
@method initWithTokens:
|
||||
@brief Creates a new token stream that @em streams the contents of the
|
||||
@c tokens array.
|
||||
|
||||
@param tokens
|
||||
The tokens to be @em streamed. All objects in the array must
|
||||
conform to the @c MPToken protocol.
|
||||
|
||||
@return A new token stream.
|
||||
*/
|
||||
- (instancetype)initWithTokens:(NSArray *)tokens; /* designated initializer */
|
||||
|
||||
|
||||
/*!
|
||||
@method beginIgnoringWhitespaceTokens
|
||||
@brief After calling this method a @c -currentToken message will skip
|
||||
whitespace characters before returning the current token.
|
||||
|
||||
@discussion Any call to this method or @c -beginAcceptingWhitespaceTokens
|
||||
must be matched by a @c -endIgnoringOrAcceptingWhitespaceTokens
|
||||
message. Calls to both methods can be nested.
|
||||
|
||||
By default whitespace tokens are accepted.
|
||||
|
||||
@see -beginAcceptingWhitespaceTokens
|
||||
@see -endIgnoringOrAcceptingWhitespaceTokens
|
||||
*/
|
||||
- (void)beginIgnoringWhitespaceTokens;
|
||||
|
||||
|
||||
/*!
|
||||
@method beginAcceptingWhitespaceTokens
|
||||
@brief After calling this method @c -currentToken may return a token of
|
||||
type @c MPWhitespaceToken.
|
||||
|
||||
@discussion Any call to this method or @c -beginAcceptingWhitespaceTokens
|
||||
must be matched by a @c -endIgnoringOrAcceptingWhitespaceTokens
|
||||
message. Calls to both methods can be nested.
|
||||
|
||||
By default whitespace tokens are accepted.
|
||||
|
||||
@see -beginIgnoringWhitespaceTokens
|
||||
@see -endIgnoringOrAcceptingWhitespaceTokens
|
||||
*/
|
||||
- (void)beginAcceptingWhitespaceTokens;
|
||||
|
||||
|
||||
/*!
|
||||
@method endIgnoringOrAcceptingWhitespaceTokens
|
||||
@brief This method balances @c -beginIgnoringWhitespaceTokens and @c
|
||||
-beginAcceptingWhitespaceTokens messages.
|
||||
|
||||
@discussion After this method was
|
||||
calles the whitespace behaviour falls back to the way it behaved
|
||||
before the last @c -beginIgnoringWhitespaceTokens or @c
|
||||
-beginAcceptingWhitespaceTokens message was sent.
|
||||
*/
|
||||
- (void)endIgnoringOrAcceptingWhitespaceTokens;
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@method hasMoreTokens
|
||||
@brief Use this method to check wether there are any tokens left.
|
||||
|
||||
@discussion This method repects the whitespace skipping behaviour. That means
|
||||
that if the token stream currently ignores whitespaces and there
|
||||
are only whitespaces left in the stream this method returns @c
|
||||
NO.
|
||||
|
||||
@return @c YES if there are more tokens, @c NO otherwise.
|
||||
*/
|
||||
- (BOOL)hasMoreTokens;
|
||||
|
||||
|
||||
/*!
|
||||
@method currentToken
|
||||
@brief Returns the receiver's current token.
|
||||
|
||||
@discussion This is one of the core methods of @c MPTokenStream. Subsequent
|
||||
calls to this method will return the same value until @c
|
||||
-currentTokenConsumed is called.
|
||||
|
||||
@return The current token or @c nil if there are no more tokens.
|
||||
*/
|
||||
- (id<MPToken>)currentToken;
|
||||
|
||||
|
||||
/*!
|
||||
@method peekNextToken
|
||||
@brief Looks at the next token and returns it.
|
||||
|
||||
@discussion This method behaves as if you called @c -currentTokenConsumed
|
||||
followed by @c -currentToken except that it does not consume the
|
||||
current token. That means that you can use this method to look at
|
||||
the next token (respecting the current whitespace ignoring
|
||||
behaviour) without changing the @c currentToken.
|
||||
|
||||
@return The next token or @c nil if there is no token after the current
|
||||
one.
|
||||
*/
|
||||
- (id<MPToken>)peekNextToken;
|
||||
|
||||
|
||||
/*!
|
||||
@method currentTokenConsumed
|
||||
@brief Marks the current token as consumed and discards it.
|
||||
*/
|
||||
- (void)currentTokenConsumed;
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,67 +10,82 @@
|
||||
|
||||
#import "MPToken.h"
|
||||
|
||||
@implementation MPTokenStream {
|
||||
NSUInteger _currentTokenIndex;
|
||||
NSUInteger eofLocation;
|
||||
|
||||
BOOL *whitespaceIgnores;
|
||||
NSUInteger maxWhitespaceIgnores;
|
||||
NSUInteger currentWhitespaceState;
|
||||
}
|
||||
|
||||
|
||||
@interface MPTokenStream ()
|
||||
|
||||
@property (nonatomic, copy) NSArray *tokens;
|
||||
@property (nonatomic) NSUInteger currentTokenIndex;
|
||||
@property (nonatomic) NSUInteger eofLocation;
|
||||
|
||||
@property (nonatomic) BOOL *whitespaceIgnores;
|
||||
@property (nonatomic) NSUInteger maxWhitespaceIgnores;
|
||||
@property (nonatomic) NSUInteger currentWhitespaceIgnoreState;
|
||||
|
||||
- (void)pushWhitespaceState:(BOOL)state;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MPTokenStream
|
||||
|
||||
- (instancetype)initWithTokens:(NSArray *)tokens
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.tokens = tokens;
|
||||
whitespaceIgnores = malloc(10 * sizeof(BOOL));
|
||||
maxWhitespaceIgnores = 10;
|
||||
currentWhitespaceState = 0;
|
||||
self.whitespaceIgnores = malloc(10 * sizeof(BOOL));
|
||||
self.maxWhitespaceIgnores = 10;
|
||||
self.currentWhitespaceIgnoreState = 0;
|
||||
[self beginAcceptingWhitespaceTokens];
|
||||
_currentTokenIndex = 0;
|
||||
if (tokens.count > 0) {
|
||||
eofLocation = NSMaxRange([tokens.lastObject range]);
|
||||
self.eofLocation = NSMaxRange([tokens.lastObject range]);
|
||||
} else {
|
||||
eofLocation = 0;
|
||||
self.eofLocation = 0;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)beginAcceptingWhitespaceTokens
|
||||
{
|
||||
[self pushWhitespaceState:YES];
|
||||
}
|
||||
|
||||
|
||||
- (void)beginIgnoringWhitespaceTokens
|
||||
{
|
||||
[self pushWhitespaceState:NO];
|
||||
}
|
||||
|
||||
|
||||
- (void)pushWhitespaceState:(BOOL)state
|
||||
{
|
||||
currentWhitespaceState++;
|
||||
if (currentWhitespaceState >= maxWhitespaceIgnores) {
|
||||
maxWhitespaceIgnores += 10;
|
||||
BOOL *reallocatedWhitespaceIgnores = realloc(whitespaceIgnores, maxWhitespaceIgnores);
|
||||
self.currentWhitespaceIgnoreState++;
|
||||
if (self.currentWhitespaceIgnoreState >= self.maxWhitespaceIgnores) {
|
||||
self.maxWhitespaceIgnores += 10;
|
||||
BOOL *reallocatedWhitespaceIgnores = realloc(self.whitespaceIgnores, self.maxWhitespaceIgnores);
|
||||
if (reallocatedWhitespaceIgnores) {
|
||||
whitespaceIgnores = reallocatedWhitespaceIgnores;
|
||||
self.whitespaceIgnores = reallocatedWhitespaceIgnores;
|
||||
} else {
|
||||
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Realloc Failed" userInfo:nil];
|
||||
}
|
||||
}
|
||||
whitespaceIgnores[currentWhitespaceState] = state;
|
||||
self.whitespaceIgnores[self.currentWhitespaceIgnoreState] = state;
|
||||
}
|
||||
|
||||
|
||||
- (void)endIgnoringOrAcceptingWhitespaceTokens
|
||||
{
|
||||
currentWhitespaceState--;
|
||||
self.currentWhitespaceIgnoreState--;
|
||||
}
|
||||
|
||||
|
||||
- (void)skipWhitespaces
|
||||
{
|
||||
if (whitespaceIgnores[currentWhitespaceState]) {
|
||||
if (self.whitespaceIgnores[self.currentWhitespaceIgnoreState]) {
|
||||
return;
|
||||
}
|
||||
while (_currentTokenIndex < self.tokens.count) {
|
||||
@@ -82,21 +97,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)hasMoreTokens
|
||||
{
|
||||
[self skipWhitespaces];
|
||||
return _currentTokenIndex < self.tokens.count;
|
||||
}
|
||||
|
||||
|
||||
- (id<MPToken>)currentToken
|
||||
{
|
||||
[self skipWhitespaces];
|
||||
if (_currentTokenIndex >= _tokens.count) {
|
||||
return [[MPToken alloc] initEOFTokenAtLocation:eofLocation];
|
||||
return nil;
|
||||
}
|
||||
return _tokens[_currentTokenIndex];
|
||||
}
|
||||
|
||||
|
||||
- (id<MPToken>)peekNextToken
|
||||
{
|
||||
NSUInteger currentTokenIndex = _currentTokenIndex;
|
||||
@@ -106,15 +124,17 @@
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
- (void)currentTokenConsumed
|
||||
{
|
||||
[self currentToken];
|
||||
++_currentTokenIndex;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
free(whitespaceIgnores);
|
||||
free(self.whitespaceIgnores);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,36 +6,158 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
@interface NSIndexPath (MPAdditions)
|
||||
|
||||
- (NSUInteger)firstIndex;
|
||||
- (NSUInteger)lastIndex;
|
||||
|
||||
- (NSIndexPath *)indexPathByReplacingLastIndexWithIndex:(NSUInteger)index;
|
||||
|
||||
/*!
|
||||
@method indexPathByRemovingFirstIndex
|
||||
@brief Provides an index path with the indexes in the receiving index path, excluding the first one.
|
||||
@discussion Returns an empty NSIndexPath instance if the receiving index path’s length is 1 or less.
|
||||
@return A new index path with the receiving index path’s indexes, excluding the first one.
|
||||
@property firstIndex
|
||||
@brief The first index from the index path.
|
||||
|
||||
@discussion If the index path is empty @c NSNotFound is returned.
|
||||
*/
|
||||
@property (readonly, nonatomic) NSUInteger firstIndex;
|
||||
|
||||
|
||||
/*!
|
||||
@property lastIndex
|
||||
@brief The last index from the index path.
|
||||
|
||||
@discussion If the index path is empty @c NSNotFound is returned.
|
||||
*/
|
||||
@property (readonly, nonatomic) NSUInteger lastIndex;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathByReplacingLastIndexWithIndex:
|
||||
@brief Provides an index path with the index in the receiving index path
|
||||
where the last one is replaced by @c index.
|
||||
|
||||
@discussion If the receiving index path is empty an index path of length @c 1
|
||||
is returned. The last index in the returned index path is @c
|
||||
index.
|
||||
|
||||
@param index
|
||||
The index with which to replace the last index in the receiving
|
||||
index path.
|
||||
|
||||
@return A new index path with @c index as its last index.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByReplacingLastIndexWithIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathByRemovingFirstIndex
|
||||
@brief Provides an index path with the indexes in the receiving index
|
||||
path, excluding the first one.
|
||||
|
||||
@discussion Returns an empty NSIndexPath instance if the receiving index
|
||||
path’s length is 1 or less.
|
||||
|
||||
@return A new index path with the receiving index path’s indexes,
|
||||
excluding the first one.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByRemovingFirstIndex;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathByPreceedingIndex:
|
||||
@brief Provides an index path with the given index followed by the indexes of the receiver.
|
||||
@discussion If the receiver does not contain any indexes the given index is the only index contained in the returned index path.
|
||||
@param index The index new index preceeding all others
|
||||
@return A new index path with all the receiver's indexes preceeded by @c index.
|
||||
@method indexPathByPreceedingIndex:
|
||||
@brief Provides an index path with the given index followed by the
|
||||
indexes of the receiver.
|
||||
|
||||
@discussion If the receiver does not contain any indexes the given index is
|
||||
the only index contained in the returned index path.
|
||||
|
||||
@param index
|
||||
The index new index preceeding all others
|
||||
|
||||
@return A new index path with all the receiver's indexes preceeded by @c
|
||||
index.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByPreceedingIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathByIncrementingLastIndex
|
||||
@brief Provides an index path with the indexes in the receiving index
|
||||
path where the last one is incremented by @c 1.
|
||||
|
||||
@discussion If the receiver does not contain any indexes an empty index path
|
||||
is returned.
|
||||
|
||||
@return A new index path with all the receiver's indexes and the last one
|
||||
incremented by @c 1.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByIncrementingLastIndex;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathByDecrementingLastIndex
|
||||
@brief Provides an index path with the indexes in the receiving index
|
||||
path where the last one is decremented by @c 1.
|
||||
|
||||
@discussion If the receiver does not contain any indexes an empty index path
|
||||
is returned.
|
||||
|
||||
@return A new index path with all the receiver's indexes and the last one
|
||||
decremented by @c 1.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByDecrementingLastIndex;
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesFrom:(NSUInteger)length; // use length indexes from the receiver, exception if too much
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesTo:(NSUInteger)length; // number of indexes from the beginning to exclude
|
||||
|
||||
/*!
|
||||
@method indexPathByRemovingIndexesFrom:
|
||||
@brief Provides an index path with the indexes in the recieving index
|
||||
path up to the index at the given position.
|
||||
|
||||
@discussion If @c from is greater or equal to the number of indexes in the
|
||||
receiving index path only the indexes to the end of the receiver
|
||||
are removed.
|
||||
|
||||
@param from
|
||||
The position of the first index to be excluded in the returned
|
||||
index path.
|
||||
|
||||
@return An index path with all indexes from the receiver up to position
|
||||
@c from.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesFrom:(NSUInteger)from;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathByRemovingIndexesTo:
|
||||
@brief Provides an index path with the indexes in the receiving index
|
||||
path where the first indexes are removed.
|
||||
|
||||
|
||||
@discussion @c to specifies the number of indexes to be removed from the
|
||||
front. Thus the index at position @c to will be included in the
|
||||
returned index path.
|
||||
|
||||
@param to
|
||||
The number of indexes to remove from the front.
|
||||
|
||||
@return A new index path with all the receiver's indexes exept the first
|
||||
@c to ones.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesTo:(NSUInteger)to;
|
||||
|
||||
|
||||
/*!
|
||||
@method commonIndexPathWith:
|
||||
@brief Provides an index path that contains the first indexes of the
|
||||
receiver that are equal to the given index path.
|
||||
|
||||
@discussion If one index path is completely included in the other a new index
|
||||
path is returned that is equal to the contained index path.
|
||||
|
||||
@param indexPath
|
||||
The index path to compare the receiver against.
|
||||
|
||||
@return A new index path with the first indexes of the receiver that are
|
||||
also present in @c indexPath.
|
||||
*/
|
||||
- (NSIndexPath *)commonIndexPathWith:(NSIndexPath *)indexPath;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,23 +8,29 @@
|
||||
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
|
||||
|
||||
@implementation NSIndexPath (MPAdditions)
|
||||
|
||||
|
||||
- (NSUInteger)firstIndex
|
||||
{
|
||||
return [self indexAtPosition:0];
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)lastIndex
|
||||
{
|
||||
return [self indexAtPosition:self.length-1];
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPathByReplacingLastIndexWithIndex:(NSUInteger)index
|
||||
{
|
||||
return [[self indexPathByRemovingLastIndex] indexPathByAddingIndex:index];
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingFirstIndex
|
||||
{
|
||||
if (self.length <= 1) {
|
||||
@@ -39,6 +45,7 @@
|
||||
return [[NSIndexPath alloc] initWithIndexes:newIndexes length:self.length-1];
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPathByPreceedingIndex:(NSUInteger)index
|
||||
{
|
||||
NSUInteger newIndexes[self.length+1];
|
||||
@@ -49,8 +56,12 @@
|
||||
return [[NSIndexPath alloc] initWithIndexes:newIndexes length:self.length+1];
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)indexPathByIncrementingLastIndex
|
||||
{
|
||||
if (self.length < 1) {
|
||||
return [[NSIndexPath alloc] init];
|
||||
}
|
||||
NSUInteger lastIndex = [self lastIndex];
|
||||
lastIndex++;
|
||||
return [[self indexPathByRemovingLastIndex] indexPathByAddingIndex:lastIndex];
|
||||
@@ -58,29 +69,35 @@
|
||||
|
||||
- (NSIndexPath *)indexPathByDecrementingLastIndex
|
||||
{
|
||||
if (self.length < 1) {
|
||||
return [[NSIndexPath alloc] init];
|
||||
}
|
||||
NSUInteger lastIndex = [self lastIndex];
|
||||
lastIndex--;
|
||||
return [[self indexPathByRemovingLastIndex] indexPathByAddingIndex:lastIndex];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesFrom:(NSUInteger)length
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesFrom:(NSUInteger)from
|
||||
{
|
||||
NSIndexPath *indexPath = [[NSIndexPath alloc] init];
|
||||
for (NSUInteger position = 0; position < length; position++) {
|
||||
for (NSUInteger position = 0; position < MIN(from, self.length); position++) {
|
||||
indexPath = [indexPath indexPathByAddingIndex:[self indexAtPosition:position]];
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesTo:(NSUInteger)length
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingIndexesTo:(NSUInteger)to
|
||||
{
|
||||
NSIndexPath *indexPath = [[NSIndexPath alloc] init];
|
||||
for (NSUInteger position = length; position < self.length; position++) {
|
||||
for (NSUInteger position = to; position < self.length; position++) {
|
||||
indexPath = [indexPath indexPathByAddingIndex:[self indexAtPosition:position]];
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
|
||||
- (NSIndexPath *)commonIndexPathWith:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSIndexPath *commonPath = [[NSIndexPath alloc] init];
|
||||
|
||||
@@ -6,14 +6,102 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#define MPStringRange(string) NSMakeRange(0, [string length])
|
||||
#define MPStringRangeFrom(from, string) NSMakeRange(from, [string length]-from)
|
||||
|
||||
|
||||
@interface NSRegularExpression (MPParsingAdditions)
|
||||
|
||||
|
||||
/*!
|
||||
@method firstMathInString:
|
||||
@brief Returns the first match of the regular expression within the
|
||||
specified string.
|
||||
|
||||
@discussion This is a convenience method that calls @c
|
||||
-firstMatchInString:options:range:
|
||||
|
||||
@param string
|
||||
The string to search.
|
||||
|
||||
@return An NSTextCheckingResult object. This result gives the overall
|
||||
matched range via its range property, and the range of each
|
||||
individual capture group via its rangeAtIndex: method. The range
|
||||
@c {NSNotFound, 0} is returned if one of the capture groups did
|
||||
not participate in this particular match.
|
||||
*/
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string;
|
||||
|
||||
|
||||
/*!
|
||||
@method firstMathInString:fromIndex
|
||||
@brief Returns the first match of the regular expression from the
|
||||
specified index in the string.
|
||||
|
||||
@discussion This is a convenience method that calls @c
|
||||
-firstMatchInString:options:range:
|
||||
|
||||
@param string
|
||||
The string to search.
|
||||
|
||||
@param start
|
||||
The index from which to start searching.
|
||||
|
||||
@return An NSTextCheckingResult object. This result gives the overall
|
||||
matched range via its range property, and the range of each
|
||||
individual capture group via its rangeAtIndex: method. The range
|
||||
@c {NSNotFound, 0} is returned if one of the capture groups did
|
||||
not participate in this particular match.
|
||||
*/
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string fromIndex:(NSUInteger)start;
|
||||
|
||||
|
||||
/*!
|
||||
@method firstMathInString:options:
|
||||
@brief Returns the first match of the regular expression within the
|
||||
specified string.
|
||||
|
||||
@discussion This is a convenience method that calls @c
|
||||
-firstMatchInString:options:range:
|
||||
|
||||
@param string
|
||||
The string to search.
|
||||
|
||||
@param options
|
||||
The matching options to use. See @c NSMatchingOptions for
|
||||
possible values.
|
||||
|
||||
@return An NSTextCheckingResult object. This result gives the overall
|
||||
matched range via its range property, and the range of each
|
||||
individual capture group via its rangeAtIndex: method. The range
|
||||
@c {NSNotFound, 0} is returned if one of the capture groups did
|
||||
not participate in this particular match.
|
||||
*/
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options;
|
||||
|
||||
|
||||
/*!
|
||||
@method firstMathInString:fromIndex
|
||||
@brief Returns the first match of the regular expression from the
|
||||
specified index in the string.
|
||||
|
||||
@discussion This is a convenience method that calls @c
|
||||
-firstMatchInString:options:range:
|
||||
|
||||
@param string
|
||||
The string to search.
|
||||
|
||||
@param options
|
||||
The matching options to use. See @c NSMatchingOptions for
|
||||
possible values.
|
||||
|
||||
@param start
|
||||
The index from which to start searching.
|
||||
|
||||
@return An NSTextCheckingResult object. This result gives the overall
|
||||
matched range via its range property, and the range of each
|
||||
individual capture group via its rangeAtIndex: method. The range
|
||||
@c {NSNotFound, 0} is returned if one of the capture groups did
|
||||
not participate in this particular match.
|
||||
*/
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options fromIndex:(NSUInteger)start;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,23 +8,28 @@
|
||||
|
||||
#import "NSRegularExpression+MPParsingAdditions.h"
|
||||
|
||||
|
||||
|
||||
@implementation NSRegularExpression (MPParsingAdditions)
|
||||
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string
|
||||
{
|
||||
return [self firstMatchInString:string options:0 range:MPStringRange(string)];
|
||||
return [self firstMatchInString:string options:0 range:NSMakeRange(0, string.length)];
|
||||
}
|
||||
|
||||
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string fromIndex:(NSUInteger)start
|
||||
{
|
||||
return [self firstMatchInString:string options:0 range:NSMakeRange(start, [string length]-start)];
|
||||
}
|
||||
|
||||
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options
|
||||
{
|
||||
return [self firstMatchInString:string options:options range:MPStringRange(string)];
|
||||
return [self firstMatchInString:string options:options range:NSMakeRange(0, string.length)];
|
||||
}
|
||||
|
||||
|
||||
- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options fromIndex:(NSUInteger)start
|
||||
{
|
||||
return [self firstMatchInString:string options:options range:NSMakeRange(start, [string length]-start)];
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
#import "MPExpressionElement.h"
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category NSString (MPExpressionElement)
|
||||
@brief This category adds @c MPExpressionElement protocol conformance to
|
||||
the @c NSString class.
|
||||
*/
|
||||
@interface NSString (MPExpressionElement) <MPExpressionElement>
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#import "NSString+MPExpressionElement.h"
|
||||
|
||||
|
||||
|
||||
@implementation NSString (MPExpressionElement)
|
||||
|
||||
- (BOOL)isString
|
||||
@@ -15,6 +17,7 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isFunction
|
||||
{
|
||||
return NO;
|
||||
|
||||
Reference in New Issue
Block a user