Archived
1
This repository has been archived on 2022-08-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mathpad/MathPad/MPExpression.h
Kim Wittenburg 82259f87e2 Model Redesign: Added Reference Frames
Added Inverse Functions
UI Redesign
Cleaned Code
2014-10-07 20:25:54 +02:00

456 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 "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;
/*!
@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;
#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