460 lines
17 KiB
Objective-C
460 lines
17 KiB
Objective-C
//
|
|
// MPExpression.h
|
|
// MathPad
|
|
//
|
|
// Created by Kim Wittenburg on 10.08.14.
|
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
|
//
|
|
|
|
@import Foundation;
|
|
#import "NSString+MPExpressionElement.h"
|
|
#import "MPExpressionTree.h"
|
|
#import "MPToken.h"
|
|
|
|
typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
|
|
MPElementReferenceFrame,
|
|
MPSymbolReferenceFrame,
|
|
MPTokenReferenceFrame
|
|
};
|
|
|
|
@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator, MPParseError;
|
|
@protocol MPExpressionElement;
|
|
|
|
/*!
|
|
@class MPExpression
|
|
@brief An @c MPExpression object is the base object for any mathematical
|
|
expression.
|
|
|
|
@discussion Every expression consists of string elements (represented by the
|
|
@c NSString class) and functions (represented by the @c
|
|
MPFunction class) elements which both can be contained within an
|
|
expression. Functions in turn can have expressions as elements
|
|
(also called 'children' in this context). Both expressions and
|
|
functions are mutable.
|
|
|
|
Through this organization expression are organized in a tree-like
|
|
structure (called the 'expression tree') allowing easy and
|
|
logical access to each element.
|
|
|
|
To query the contents of an expressions there are two options:
|
|
You can query on a per-element basis (that is string elements and
|
|
function elements as described before). This is called the @em
|
|
index reference frame (or @em indexed reference frame). You also
|
|
can query an expression on a per-symbol basis. This is called the
|
|
@em location reference frame (or @c located reference frame).
|
|
Using the located reference frame is useful if you are dealing
|
|
with user actions since the user does not necessarily see a
|
|
visual separation between different elements but only sees the
|
|
symbols that are drawn on the screen. You can convert between the
|
|
two reference frames using the following methods:
|
|
@code
|
|
- (NSUInteger)indexOfElementAtSymbolLocation:offset: // Converts from the located to the indexed reference frame
|
|
- (NSUInteger)locationOfElementAtIndex: // Converts from the indexed to the located reference frame
|
|
@endcode
|
|
|
|
In the indexed reference frame both functions and strings have a
|
|
dimension of 1. In the located reference frame the length of a
|
|
string is determined by sending it a @c -length message. In that
|
|
frame functions still have a length of 1.
|
|
|
|
An expression can evaluate itself giving you either a
|
|
result or possibly an error if the expression was not constructed
|
|
correctly.
|
|
*/
|
|
@interface MPExpression : NSObject <NSCopying, NSCoding>
|
|
|
|
|
|
#pragma mark Creation Methods
|
|
|
|
|
|
/*!
|
|
@method init
|
|
@brief Initlializes a newly created expression.
|
|
|
|
@discussion This method is a convenience initializer to initialize an
|
|
expression with @c 0 elements.
|
|
|
|
@return An expression.
|
|
*/
|
|
- (instancetype)init;
|
|
|
|
- (instancetype)initWithExpressionTree:(MPExpressionTree *)expressionTree;
|
|
|
|
|
|
/*!
|
|
@method initWithElement:
|
|
@brief Initializes a newly created expression with @c element.
|
|
|
|
@discussion This method is a convenience initializer to initialize an
|
|
expression with a single element.
|
|
|
|
@param element
|
|
The element to be added to the expression. The @c element will be
|
|
copied.
|
|
|
|
@return An expression initialized with @c element.
|
|
*/
|
|
- (instancetype)initWithElement:(id<MPExpressionElement>)element;
|
|
|
|
|
|
/*!
|
|
@method initWithElements:
|
|
@brief Initializes a newly created expression with the given elements.
|
|
|
|
@discussion All elements must conform to the @c MPExpressionElement protocol.
|
|
If one or more objects do not conform to that protocol an @c
|
|
MPIllegalElementException is raised.
|
|
|
|
This method is the designated initializer for the @c MPExpression
|
|
class.
|
|
|
|
@param elements
|
|
The elements that should be added to the expression. Every
|
|
element must conform to the @c MPExpressionElement protocol. Each
|
|
element is copied and the copy is then added to the expression.
|
|
The object in the @c elements array is not modified.
|
|
|
|
@return An expression containing the elements from @c elements.
|
|
*/
|
|
- (instancetype)initWithElements:(NSArray *)elements; /* designated initializer */
|
|
|
|
|
|
#pragma mark Querying Expressions
|
|
|
|
|
|
/*!
|
|
@property parent
|
|
@brief The receiver's parent.
|
|
|
|
@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.
|
|
|
|
@return The parent of the receiver or @c nil if the receiver is the root
|
|
expression.
|
|
*/
|
|
@property (nonatomic, weak) MPFunction *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.
|
|
*/
|
|
- (MPExpression *)rootExpression;
|
|
|
|
|
|
/*!
|
|
@method indexPath
|
|
@brief Returns the index path of the receiver in the expression tree.
|
|
|
|
@discussion The index path is calculated by going up the expression tree
|
|
collecting the respective index of the receiver. The indexes are
|
|
expressed in the indexed reference frame. If any of the indexes
|
|
exceed the respective receiver's bounds a @c NSRangeException is
|
|
raised.
|
|
|
|
@return The index path of the receiver in the expression tree.
|
|
*/
|
|
- (NSIndexPath *)indexPath;
|
|
|
|
|
|
/*!
|
|
@method numberOfElements
|
|
@brief Returns the number of elements in the receiver.
|
|
|
|
@discussion The number of elements may vary from the number of elements that
|
|
were added to the receiver (using either @c -initWithElements: or
|
|
one of the several mutation methods). The number of elements is
|
|
expressed in the indexed reference frame. (The respective method
|
|
for the located reference frame is @c -length).
|
|
|
|
@return The current number of elements in the receiver.
|
|
*/
|
|
- (NSUInteger)countItemsInReferenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
|
|
/*!
|
|
@method elementAtIndex:
|
|
@brief Returns the element at @c anIndex.
|
|
|
|
@discussion The element 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.
|
|
|
|
@return The element at @c anIndex.
|
|
*/
|
|
- (id)itemAtIndex:(NSUInteger)anIndex
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
/*!
|
|
@method indexOfElement:
|
|
@brief Returns the index of @c element or @c NSNotFound if it was not
|
|
found.
|
|
|
|
@param element
|
|
The element to find.
|
|
|
|
@return The index of @c element expressed in the indexed 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.
|
|
|
|
@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.
|
|
|
|
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.
|
|
|
|
@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
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
|
|
/*!
|
|
@method elements
|
|
@brief Returns an array of all elements in the receiver.
|
|
|
|
@discussion The elements in the returned array are not copied before they are
|
|
returned.
|
|
|
|
@return An array of all elements from the receiver.
|
|
*/
|
|
- (NSArray *)allItemsInReferenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
|
|
/*!
|
|
@method elementAtIndexPath:
|
|
@brief Returns the element at the specified index path.
|
|
|
|
@discussion This method @em walks down the expression tree (including
|
|
functions) using the specified index path and finds the
|
|
corresponding element. The returned object can be an @c NSString,
|
|
a @c MPFunction or an @c MPExpression depending on the element @c
|
|
indexPath points to. If any of the indexes exceed the bounds of
|
|
the respective receiver an @c NSRangeException is raised.
|
|
|
|
If the index path does not contain any indexes the receiver
|
|
itself is returned.
|
|
|
|
@param indexPath
|
|
The index path the required object is located at. The indexes are
|
|
expressed in the indexed reference frame.
|
|
|
|
@return The element located at @c indexPath. The element is not copied
|
|
before it is returned. Be aware of the fact that any mutations
|
|
made to the returned object are reflected in the receiver.
|
|
*/
|
|
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
|
|
|
- (NSUInteger)convertIndex:(NSUInteger)index
|
|
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
|
toReferenceFrame:(MPReferenceFrame)toReferenceFrame;
|
|
|
|
- (NSUInteger)convertIndex:(NSUInteger)index
|
|
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
|
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
|
offset:(NSUInteger *)offset;
|
|
|
|
- (NSRange)convertRange:(NSRange)aRange
|
|
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
|
toReferenceFrame:(MPReferenceFrame)toReferenceFrame;
|
|
|
|
- (NSRange)convertRange:(NSRange)aRange
|
|
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
|
|
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
|
|
leadingOffset:(NSUInteger *)leadingOffset
|
|
trailingOffset:(NSUInteger *)trailingOffset;
|
|
|
|
|
|
#pragma mark Mutating Expressions
|
|
|
|
|
|
/*!
|
|
@method replaceSymbolsInRange:withElements:
|
|
@brief Replaces the elements in the given range with the contents of the
|
|
@c elements array.
|
|
|
|
@discussion This is the most primitive mutation method of @c MPExpression.
|
|
Every other mutating method utlimately must call this method.
|
|
|
|
|
|
After the receiver has been mutated the integrety of the receiver
|
|
is restored. That basically means that subsequent strings are
|
|
joined and empty strings removed. After restoring integrity the
|
|
receiver sends a @c
|
|
-didChangeElementsInIndexedRangePath:replacementLength: to
|
|
itself. For more information see the documentation on that
|
|
method.
|
|
|
|
@param range
|
|
The @c range of symbols (including functions) to replace
|
|
specified in the located reference frame.
|
|
|
|
@param elements
|
|
The elements that should replace the symbols specified by @c
|
|
range.
|
|
*/
|
|
- (void)replaceItemsInRange:(NSRange)range
|
|
referenceFrame:(MPReferenceFrame)referenceFrame
|
|
withElements:(NSArray *)elements;
|
|
|
|
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
- (MPExpression *)subexpressionWithRange:(NSRange)range
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
#pragma mark Evaluating Expressions
|
|
|
|
|
|
/*!
|
|
@method evaluateWitError:
|
|
@brief Evaluates the receiving expression.
|
|
|
|
@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.
|
|
|
|
@param error
|
|
If the receiver (or any of its elements) contains a syntax error
|
|
or can not be evaluated this parameter is set to an appropriate
|
|
value. Pass @c NULL if you are not interested in any errors that
|
|
might occur.
|
|
|
|
@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.
|
|
*/
|
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
|
|
|
|
- (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
|
|
|
|
|
|
/*!
|
|
@method didChangeElementsInIndexedRangePath:replacementLength:
|
|
@brief Called after the receiver has been mutated.
|
|
|
|
@discussion This method does nothing more than notify it's parent that it has
|
|
been mutated at the receiver's index. If you need to know about
|
|
changes in an expression you should override this method instead
|
|
of @c -replaceSymbolsInRange:withElements: because this method
|
|
gives you information about the number of elements changed during
|
|
the mutation.
|
|
|
|
@param rangePath
|
|
The range path at which the receiver was changed starting at the
|
|
receiver. The range addressed by @c rangePath is expressed in the
|
|
indexed reference frame.
|
|
|
|
@param replacementLength
|
|
The number of elements replacing the elements specified by @c
|
|
rangePath.
|
|
*/
|
|
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
|
replacementLength:(NSUInteger)replacementLength;
|
|
|
|
|
|
#pragma mark Basic NSObject Methods
|
|
// TODO: Check this
|
|
// - (BOOL)isEqualToExpression:(MPExpression *)anExpression;
|
|
|
|
@end
|
|
|
|
@interface MPExpression (MPExpressionConvenience)
|
|
|
|
|
|
#pragma mark Querying Expressions
|
|
|
|
- (NSUInteger)countElements;
|
|
- (NSUInteger)countSymbols;
|
|
- (NSUInteger)countTokens;
|
|
|
|
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index;
|
|
- (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)index;
|
|
- (id<MPToken>)tokenAtIndex:(NSUInteger)index;
|
|
|
|
|
|
#pragma mark Mutating Expressions
|
|
|
|
/*!
|
|
@method appendElement:
|
|
@brief Appends @c anElement 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.
|
|
|
|
@param elements
|
|
The elements to append to the receiver.
|
|
*/
|
|
- (void)appendElements:(NSArray *)elements;
|
|
|
|
- (void)insertElement:(id<MPExpressionElement>)anElement
|
|
atIndex:(NSUInteger)index
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
- (void)insertElements:(NSArray *)elements
|
|
atIndex:(NSUInteger)index
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
|
|
/*!
|
|
@method deleteElementsInRange:
|
|
@brief Removes the elements specified by @c range from the receiver.
|
|
|
|
@discussion The range is specified in the length reference frame.
|
|
|
|
If @c range exceeds the receiver's bounds a @c NSRangeException
|
|
is raised.
|
|
|
|
@param range
|
|
The range to remove from the receiver.
|
|
*/
|
|
- (void)deleteElementsInRange:(NSRange)range
|
|
referenceFrame:(MPReferenceFrame)referenceFrame;
|
|
|
|
@end
|