Project Update (stable)
This commit is contained in:
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user