Archived
1

Project Update (stable)

This commit is contained in:
Kim Wittenburg
2014-11-24 22:44:07 +01:00
parent 6067600e81
commit b4111c69e4
30 changed files with 120 additions and 1600 deletions

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6245"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>

View File

@@ -1,79 +0,0 @@
//
// MPExpressionTree.h
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#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;
@end

View File

@@ -1,125 +0,0 @@
//
// MPExpressionTree.m
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionTree.h"
#import "MPSummand.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
@implementation MPExpressionTree {
NSMutableArray *_summands;
}
- (id)init
{
self = [super init];
if (self) {
_summands = [[NSMutableArray alloc] init];
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
self.definedVariable = nil;
if (tokenStream.currentToken.tokenType == MPVariableToken) {
if (tokenStream.peekNextToken.tokenType == MPEqualsToken) {
self.definedVariable = tokenStream.currentToken.stringValue;
[tokenStream currentTokenConsumed];
[tokenStream currentTokenConsumed];
}
}
while ([tokenStream hasMoreTokens]) {
[_summands addObject:[[MPSummand alloc] initWithTokenStream:tokenStream]];
}
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
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
{
if (flag) {
if (!self.definedVariable) {
if (error) {
*error = MPParseError(1,
NSLocalizedString(@"Expected Variable Definition.", @"Error message. This is displayed when an expected variable definition was not found."),
nil);
}
return NO;
}
} else {
if (self.definedVariable) {
if (error) {
*error = MPParseError(2,
NSLocalizedString(@"Unexpected Variable Definition.", @"Error message. This is displayed when no variable definition was expected but one was found."),
nil);
}
return NO;
}
}
if (_summands.count == 0) {
if (error) {
*error = MPParseError(3,
NSLocalizedString(@"Empty Expression.", @"Error message. This is displayed when the user tried to evaluate an empty expression."),
nil);
}
return NO;
}
for (MPSummand *summand in _summands) {
if (![summand validate:error]) {
return NO;
}
}
return YES;
}
- (NSDecimalNumber *)evaluate
{
NSDecimalNumber *value = [NSDecimalNumber zero];
for (MPSummand *summand in _summands) {
NSDecimalNumber *currentValue = [summand evaluate];
if ([currentValue isEqualToNumber:[NSDecimalNumber notANumber]]) {
value = currentValue;
break;
}
value = [value decimalNumberByAdding:currentValue];
}
return value;
}
@end

View File

@@ -1,89 +0,0 @@
//
// MPExpressionTreeElement.h
// MathPad
//
// 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
/*!
@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;
/*!
@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

View File

@@ -1,20 +0,0 @@
//
// MPFactor.h
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionTreeElement.h"
@class MPFactor, MPOperatorChain;
@protocol MPValue;
@interface MPFactor : NSObject <MPExpressionTreeElement>
@property (readonly, nonatomic) BOOL hasMultiplicationSymbol;
@property (readonly, nonatomic, strong) MPOperatorChain *operatorChain;
@property (readonly, nonatomic, strong) id<MPValue> value;
@end

View File

@@ -1,80 +0,0 @@
//
// MPFactor.m
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFactor.h"
#import "MPOperatorChain.h"
#import "MPValueGroup.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
#import "MPMathRules.h"
@implementation MPFactor {
BOOL _multiplicationSymbolPresent;
}
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
MPToken *token = tokenStream.currentToken;
_multiplicationSymbolPresent = token.tokenType == MPMultiplicationSymbolToken;
if (_multiplicationSymbolPresent) {
[tokenStream currentTokenConsumed];
}
if (tokenStream.currentToken.tokenType == MPOperatorListToken) {
_operatorChain = [[MPOperatorChain alloc] initWithTokenStream:tokenStream];
}
MPValueGroup *valueGroup = [[MPValueGroup alloc] initWithTokenStream:tokenStream];
_value = [tokenStream consumeSuffixesForValue:valueGroup];
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (BOOL)hasMultiplicationSymbol
{
return _multiplicationSymbolPresent;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
if (self.operatorChain.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) {
if (error) {
*error = MPParseError(7,
NSLocalizedString(@"Too many operators in Multiplication.", @"Error message. This is displayed when there are too many operators between a multiplication symbol and a value."),
nil);
}
return NO;
}
return [self.value validate:error];
}
- (NSDecimalNumber *)evaluate
{
NSDecimalNumber *value = [self.value evaluate];
if ([value isNotEqualTo:[NSDecimalNumber notANumber]] && self.operatorChain) {
value = [value decimalNumberByMultiplyingBy:[self.operatorChain evaluate]];
}
return value;
}
@end

View File

@@ -1,17 +0,0 @@
//
// MPFactorial.h
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPValueGroup.h"
@class MPFactorial;
@interface MPFactorial : NSObject <MPValue>
@property (readonly, nonatomic, strong) id<MPValue> value;
@end

View File

@@ -1,51 +0,0 @@
//
// MPFactorial.m
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFactorial.h"
#import "MPTokenStream.h"
#import "MPToken.h"
@implementation MPFactorial
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
if (tokenStream.currentToken.tokenType != MPFactorialToken) {
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
return nil;
} else {
[tokenStream currentTokenConsumed];
}
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
return [self.value validate:error];
}
- (NSDecimalNumber *)evaluate
{
double value = [self.value evaluate].doubleValue;
return [[NSDecimalNumber alloc] initWithDouble:tgamma(value+1)];
}
@end

View File

@@ -1,14 +0,0 @@
//
// MPFunction+MPValue.h
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunction.h"
#import "MPValueGroup.h"
@interface MPFunction (MPValue) <MPValue>
@end

View File

@@ -1,28 +0,0 @@
//
// MPFunction+MPValue.m
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunction+MPValue.h"
#import "MPFunction+MPToken.h"
@implementation MPFunction (MPValue)
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
[tokenStream beginIgnoringWhitespaceTokens];
if (tokenStream.currentToken.tokenType != MPGenericFunctionToken) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"Expected Function"
userInfo:nil];
}
[tokenStream currentTokenConsumed];
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
return [self init];
}
@end

View File

@@ -1,19 +0,0 @@
//
// MPFunctionValue.h
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPValueGroup.h"
@class MPFunctionValue, MPPowerFunction, MPValueGroup;
@interface MPFunctionValue : NSObject <MPValue>
@property (readonly, nonatomic, copy) NSString *functionName;
@property (readonly, nonatomic, strong) MPPowerFunction *power; // May be nil
@property (readonly, nonatomic, strong) MPValueGroup *valueGroup;
@end

View File

@@ -1,145 +0,0 @@
//
// MPFunctionValue.m
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunctionValue.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
#import "MPMathRules.h"
double defaultFunction(double value) {return value;};
@implementation MPFunctionValue {
NSDecimalNumber *(^_function)(NSDecimalNumber *);
}
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
switch (tokenStream.currentToken.tokenType) {
case MPSinToken:
[tokenStream currentTokenConsumed];
_functionName = @"sin";
return [self initWithTokenStream:tokenStream
function:&sin
takesArcValue:YES
returnsArcValue:NO];
case MPCosToken:
[tokenStream currentTokenConsumed];
_functionName = @"cos";
return [self initWithTokenStream:tokenStream
function:&cos
takesArcValue:YES
returnsArcValue:NO];
case MPTanToken:
[tokenStream currentTokenConsumed];
_functionName = @"tan";
return [self initWithTokenStream:tokenStream
function:&tan
takesArcValue:YES
returnsArcValue:NO];
case MPASinToken:
[tokenStream currentTokenConsumed];
_functionName = @"asin";
return [self initWithTokenStream:tokenStream
function:&asin
takesArcValue:YES
returnsArcValue:NO];
case MPACosToken:
[tokenStream currentTokenConsumed];
_functionName = @"acos";
return [self initWithTokenStream:tokenStream
function:&acos
takesArcValue:YES
returnsArcValue:NO];
case MPATanToken:
[tokenStream currentTokenConsumed];
_functionName = @"atan";
return [self initWithTokenStream:tokenStream
function:&atan
takesArcValue:YES
returnsArcValue:NO];
default:
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"Expected Function"
userInfo:nil];
break;
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
function:(double (*)(double))function
takesArcValue:(BOOL)takesArc
returnsArcValue:(BOOL)returnsArc
{
self = [self init];
if (self) {
_valueGroup = [[MPValueGroup alloc] initWithTokenStream:tokenStream];
id __weak weakSelf = self;
_function = ^(NSDecimalNumber *value){
if (takesArc) {
value = [weakSelf convertToRadiantsIfNecessary:value];
}
NSDecimalNumber *returnValue = [[NSDecimalNumber alloc] initWithDouble:function(value.doubleValue)];
if (returnsArc) {
returnValue = [weakSelf convertToDegreesIfNecessary:returnValue];
}
return returnValue;
};
}
return self;
}
- (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees
{
if ([MPMathRules sharedRules].isUsingDegrees) {
// * M_PI / 180
return [[degrees decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithInteger:180]];
} else {
return degrees;
}
}
- (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants
{
if ([MPMathRules sharedRules].isUsingDegrees) {
// * 180 / M_PI
return [[radiants decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:180]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]];
} else {
return radiants;
}
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
return [self.valueGroup validate:error];
}
- (NSDecimalNumber *)evaluate
{
return _function([self.valueGroup evaluate]);
}
@end

View File

@@ -1,18 +0,0 @@
//
// MPOperatorChain.h
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionTreeElement.h"
@class MPOperatorChain;
@interface MPOperatorChain : NSObject <MPExpressionTreeElement>
@property (readonly, nonatomic) BOOL negating;
@property (readonly, nonatomic) NSUInteger numberOfOperators;
@end

View File

@@ -1,61 +0,0 @@
//
// MPOperatorChain.m
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPOperatorChain.h"
#import "MPTokenStream.h"
#import "MPToken.h"
@implementation MPOperatorChain
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
MPToken *currentToken = tokenStream.currentToken;
if (currentToken.tokenType != MPOperatorListToken) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Operators" userInfo:nil];
}
NSString *operatorString = currentToken.stringValue;
operatorString = [[operatorString componentsSeparatedByString:@" "] componentsJoinedByString:@""];
_negating = NO;
_numberOfOperators = operatorString.length;
for (NSUInteger index = 0; index < _numberOfOperators; index++) {
if ([[operatorString substringWithRange:NSMakeRange(index, 1)] isEqualToString:@"-"]) {
_negating = !_negating;
}
}
[tokenStream currentTokenConsumed];
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
return YES;
}
- (NSDecimalNumber *)evaluate
{
return self.negating ? [[NSDecimalNumber alloc] initWithInteger:-1] : [NSDecimalNumber one];
}
@end

View File

@@ -1,17 +0,0 @@
//
// MPProduct.h
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionTreeElement.h"
@class MPProduct;
@interface MPProduct : NSObject <MPExpressionTreeElement>
@property (readonly, nonatomic, strong) NSArray *factors;
@end

View File

@@ -1,99 +0,0 @@
//
// MPProduct.m
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPProduct.h"
#import "MPFactor.h"
#import "MPValueGroup.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
@implementation MPProduct {
NSMutableArray *_factors;
}
- (id)init
{
self = [super init];
if (self) {
_factors = [[NSMutableArray alloc] init];
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
while ([tokenStream hasMoreTokens] && tokenStream.currentToken.tokenType != MPOperatorListToken) {
[_factors addObject:[[MPFactor alloc] initWithTokenStream:tokenStream]];
}
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (NSArray *)factors
{
return _factors;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
if (_factors.count == 0) {
if (error) {
*error = MPParseError(5,
NSLocalizedString(@"Expected A Value.", @"Error message. Displayed when a value was expected in an expression but none was found."),
nil);
}
return NO;
}
MPFactor *factor = _factors[0];
if (factor.hasMultiplicationSymbol) {
if (error) {
*error = MPParseError(6,
NSLocalizedString(@"Unexpected Symbol.", @"Error message. Displayed when or inappropriate symbol was encountered during parsing."),
nil);
}
return NO;
}
if (![factor.value validate:error]) {
return NO;
}
for (NSUInteger index = 1; index < _factors.count; index++) {
factor = _factors[index];
if (![factor validate:error]) {
return NO;
}
}
return YES;
}
- (NSDecimalNumber *)evaluate
{
NSDecimalNumber *value = [NSDecimalNumber one];
for (MPFactor *factor in _factors) {
NSDecimalNumber *currentValue = [factor evaluate];
if ([currentValue isEqualToNumber:[NSDecimalNumber notANumber]]) {
value = currentValue;
break;
}
value = [value decimalNumberByMultiplyingBy:currentValue];
if ([value compare:@(0)] == NSOrderedSame) {
break;
}
}
return value;
}
@end

View File

@@ -1,18 +0,0 @@
//
// MPRootFunction.h
// MathPad
//
// Created by Kim Wittenburg on 22.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunction.h"
@class MPRootFunction, MPExpression;
@interface MPRootFunction : MPFunction
@property (nonatomic, strong) MPExpression *exponentExpression;
@property (nonatomic, strong) MPExpression *radicantExpression;
@end

View File

@@ -1,44 +0,0 @@
//
// MPRootFunction.m
// MathPad
//
// Created by Kim Wittenburg on 22.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPRootFunction.h"
#import "MPExpression.h"
#import "MPExpressionTree.h"
@implementation MPRootFunction
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
MPFunctionAccessorImplementation(RadicantExpression, _radicantExpression)
- (NSArray *)childrenAccessors
{
return @[@"exponentExpression", @"radicantExpression"];
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
return [[self.exponentExpression parse] validate:error] && [[self.radicantExpression parse] validate:error];
}
- (NSDecimalNumber *)evaluate
{
NSDecimalNumber *exponent = [[self.exponentExpression parse] evaluate];
NSDecimalNumber *radicant = [[self.radicantExpression parse] evaluate];
return [[NSDecimalNumber alloc] initWithDouble:pow(radicant.doubleValue, exponent.doubleValue)];
}
- (NSString *)description
{
if (self.exponentExpression == nil) {
return [NSString stringWithFormat:@"√%@", self.radicantExpression.description];
}
return [NSString stringWithFormat:@"pow(%@; %@)", self.radicantExpression.description, self.exponentExpression];
}
@end

View File

@@ -1,19 +0,0 @@
//
// MPRootFunctionLayout.h
// MathPad
//
// Created by Kim Wittenburg on 22.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunctionLayout.h"
@class MPRootFunctionLayout, MPRootFunction;
@interface MPRootFunctionLayout : MPFunctionLayout
- (MPRootFunction *)rootFunction;
@property (nonatomic, getter=isExponentImplicit) BOOL exponentImplicit;
@end

View File

@@ -1,84 +0,0 @@
//
// MPRootFunctionLayout.m
// MathPad
//
// Created by Kim Wittenburg on 22.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPRootFunctionLayout.h"
#import "MPRootFunction.h"
#define kRootFunctionSpaceBetweenRadicantAndExponent 5
@implementation MPRootFunctionLayout
- (MPRootFunction *)rootFunction
{
return (MPRootFunction *)self.function;
}
- (NSUInteger)indexOfLeadingChild
{
return 0;
}
- (NSUInteger)indexOfTrailingChild
{
return 1;
}
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{
return index == 0 ? 1 : [super indexOfChildAfterChildAtIndex:index];
}
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
{
return index == 1 ? 0 : [super indexOfChildBeforeChildAtIndex:index];
}
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{
return 1;
}
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{
return 0;
}
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSetWithIndex:1];
}
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
NSRect radicandBounds = [self childLayoutAtIndex:1].bounds;
if (index == 0) {
return NSMakePoint(0, (radicandBounds.size.height + radicandBounds.origin.y) / 2);
} else {
return NSMakePoint(exponentBounds.size.width + kRootFunctionSpaceBetweenRadicantAndExponent, 0);
}
}
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
return index == 0;
}
- (NSRect)generateBounds
{
return NSZeroRect;
}
- (void)draw
{
}
@end

View File

@@ -1,44 +0,0 @@
//
// MPSuffixFunction.h
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#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

View File

@@ -1,15 +0,0 @@
//
// MPSuffixFunction.m
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPSuffixFunction.h"
@implementation MPSuffixFunction
@end

View File

@@ -1,42 +0,0 @@
//
// MPSummand.h
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#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

View File

@@ -1,66 +0,0 @@
//
// MPSummand.m
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPSummand.h"
#import "MPOperatorChain.h"
#import "MPProduct.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
#import "MPMathRules.h"
@implementation MPSummand
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
if (tokenStream.currentToken.tokenType == MPOperatorListToken) {
_operatorChain = [[MPOperatorChain alloc] initWithTokenStream:tokenStream];
}
_product = [[MPProduct alloc] initWithTokenStream:tokenStream];
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
if (self.operatorChain.numberOfOperators > [MPMathRules sharedRules].maximumOperatorChainLength) {
if (error) {
*error = MPParseError(4,
NSLocalizedString(@"Too many operators.", @"Error message. This is displayed when there are too many subsequent operators in an expression."),
nil);
}
return NO;
}
return [self.product validate:error];
}
- (NSDecimalNumber *)evaluate
{
NSDecimalNumber *value = [self.product evaluate];
if ([value isNotEqualTo:[NSDecimalNumber notANumber]] && self.operatorChain) {
value = [value decimalNumberByMultiplyingBy:[self.operatorChain evaluate]];
}
return value;
}
@end

View File

@@ -1,17 +0,0 @@
//
// MPUnidentifiedSymbolValue.h
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPValueGroup.h"
@class MPUnexpectedSymbolValue;
@interface MPUnexpectedSymbolValue : NSObject <MPValue>
@property (readonly, nonatomic, copy) NSString *symbol;
@end

View File

@@ -1,58 +0,0 @@
//
// MPUnidentifiedSymbolValue.m
// MathPad
//
// Created by Kim Wittenburg on 11.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPUnexpectedSymbolValue.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
@implementation MPUnexpectedSymbolValue
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
_symbol = tokenStream.currentToken.stringValue;
[tokenStream currentTokenConsumed];
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
if (error) {
*error = MPParseError(10,
NSLocalizedString(@"Unknown Symbol.", @"Error message. Displayed when an unknown symbol was encountered during parsing."),
nil);
}
return NO;
}
- (NSDecimalNumber *)evaluate
{
return [NSDecimalNumber notANumber];
}
- (NSArray *)expressionElements
{
return @[self.symbol];
}
@end

View File

@@ -1,31 +0,0 @@
//
// MPValueGroup.h
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionTreeElement.h"
#import "MPTokenStream.h"
@class MPValueGroup;
@protocol MPValue;
@protocol MPValue <MPExpressionTreeElement>
@end
@interface MPValueGroup : NSObject <MPValue>
@property (readonly, nonatomic, strong) NSArray *values;
@end
@interface MPTokenStream (MPValueSuffixes)
- (id<MPValue>)consumeSuffixesForValue:(id<MPValue>)value;
@end

View File

@@ -1,162 +0,0 @@
//
// MPValueGroup.m
// MathPad
//
// Created by Kim Wittenburg on 09.10.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPValueGroup.h"
#import "MPNumber.h"
#import "MPVariable.h"
#import "MPFunctionValue.h"
#import "MPUnexpectedSymbolValue.h"
#import "MPFactorial.h"
#import "MPTokenStream.h"
#import "MPToken.h"
#import "MPExpression.h"
#import "MPSuffixFunction.h"
@implementation MPValueGroup {
NSMutableArray *_values;
}
- (id)init
{
self = [super init];
if (self) {
_values = [[NSMutableArray alloc] init];
}
return self;
}
- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream
{
self = [self init];
if (self) {
[tokenStream beginIgnoringWhitespaceTokens];
[tokenStream currentToken]; // This will skip leading whitespaces
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
[tokenStream beginAcceptingWhitespaceTokens];
BOOL checkMoreTokens = YES;
while (tokenStream.currentToken.tokenType != MPWhitespaceToken && checkMoreTokens) {
id<MPValue> currentValue;
switch (tokenStream.currentToken.tokenType) {
case MPNumberToken:
{
currentValue = [[MPNumber alloc] initWithTokenStream:tokenStream];
}
break;
case MPVariableToken:
{
currentValue = [[MPVariable alloc] initWithTokenStream:tokenStream];
}
break;
case MPGenericFunctionToken:
{
currentValue = (id<MPValue>)tokenStream.currentToken;
if ([currentValue isKindOfClass:[MPSuffixFunction class]]) {
((MPSuffixFunction *)currentValue).baseValue = nil;
}
[tokenStream currentTokenConsumed];
}
break;
case MPSinToken:
case MPCosToken:
case MPTanToken:
case MPASinToken:
case MPACosToken:
case MPATanToken:
{
currentValue = [[MPFunctionValue alloc] initWithTokenStream:tokenStream];
}
break;
case MPUnidentifiedToken:
case MPEqualsToken:
currentValue = [[MPUnexpectedSymbolValue alloc] initWithTokenStream:tokenStream];
break;
default:
checkMoreTokens = NO;
break;
}
if (checkMoreTokens) {
[_values addObject:[tokenStream consumeSuffixesForValue:currentValue]];
}
}
[tokenStream endIgnoringOrAcceptingWhitespaceTokens];
}
return self;
}
- (BOOL)validate:(NSError *__autoreleasing *)error
{
if (_values.count == 0) {
if (error) {
*error = MPParseError(8,
NSLocalizedString(@"Expected Value.", @"Error message. This is displayed when a value was expected but none was found."),
nil);
}
return NO;
}
for (id<MPValue> value in _values) {
if (![value validate:error]) {
return NO;
}
}
return YES;
}
- (NSDecimalNumber *)evaluate
{
NSDecimalNumber *result = [NSDecimalNumber one];
for (id<MPValue> value in _values) {
NSDecimalNumber *currentValue = [value evaluate];
if ([currentValue isEqualToNumber:[NSDecimalNumber notANumber]]) {
result = currentValue;
break;
}
result = [result decimalNumberByMultiplyingBy:currentValue];
if ([result compare:@(0)] == NSOrderedSame) {
break;
}
}
return result;
}
@end
@implementation MPTokenStream (MPValueSuffixes)
- (id<MPValue>)consumeSuffixesForValue:(id<MPValue>)value
{
BOOL repeat = YES;
while (repeat) {
if (self.currentToken.tokenType == MPFactorialToken) {
MPFactorial *factorial = [[MPFactorial alloc] init];
factorial.value = value;
value = factorial;
[self currentTokenConsumed];
} else if (self.currentToken.tokenType == MPGenericFunctionToken && [self.currentToken isKindOfClass:[MPSuffixFunction class]]) {
MPSuffixFunction *token = (MPSuffixFunction *)self.currentToken;
token.baseValue = value;
value = token;
[self currentTokenConsumed];
} else {
repeat = NO;
}
}
return value;
}
@end