Archived
1

Implemented Evaluation

This commit is contained in:
Kim Wittenburg
2014-09-07 16:45:31 +02:00
parent 8df8317413
commit 21bfe221ba
13 changed files with 593 additions and 96 deletions

18
MathPad/MPElementParser.h Normal file
View File

@@ -0,0 +1,18 @@
//
// MPElementParser.h
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPParsedElement.h"
#import "MPParseError.h"
@interface MPElementParser : NSObject
- (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error;
@end

185
MathPad/MPElementParser.m Normal file
View File

@@ -0,0 +1,185 @@
//
// MPElementParser.m
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPElementParser.h"
@implementation MPElementParser {
NSScanner *scanner;
}
- (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error
{
// Setup the Scanner
scanner = [[NSScanner alloc] initWithString:string];
scanner.charactersToBeSkipped = [NSCharacterSet whitespaceAndNewlineCharacterSet];
scanner.caseSensitive = YES;
MPParsedElement *parsedElement = [[MPParsedElement alloc] init];
// Scan the defined variable
NSString *firstCharacters = nil;
BOOL hasLettersInFront = [scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet]
intoString:&firstCharacters];
BOOL foundEquals = [scanner scanString:@"="
intoString:NULL];
if (hasLettersInFront && firstCharacters.length == 1 && foundEquals) {
// Found variable definition
parsedElement.definedVariable = firstCharacters;
parsedElement.afterVariableDefinitionIndex = scanner.scanLocation;
} else {
// No variable definition found, reset the scanner
parsedElement.definedVariable = nil;
scanner.scanLocation = 0;
}
// Scan the Prefix
BOOL startsWithAsterisk = [scanner scanString:@"*"
intoString:NULL];
BOOL productClosed;
NSDecimalNumber *prefix = [self scanClosedProduct:&productClosed
error:NULL];
// Simple Factor
if (scanner.isAtEnd) {
parsedElement.isFactor = YES;
parsedElement.value = prefix == nil ? [NSDecimalNumber one] : prefix;
parsedElement.prefixOperatorExplicit = startsWithAsterisk;
parsedElement.suffixOperatorExplicit = !productClosed;
parsedElement.suffixIndex = scanner.scanLocation;
return parsedElement;
} else if (!productClosed) {
if (error) {
*error = MPParseError(scanner.scanLocation, @"Missing Number");
}
return nil;
}
parsedElement.prefixOperatorExplicit = startsWithAsterisk;
parsedElement.prefixValueExplicit = YES;
parsedElement.prefixMultiplicator = prefix;
parsedElement.suffixValueExplicit = YES;
// Scan Summands
NSDecimalNumber *value = [NSDecimalNumber zero];
NSDecimalNumber *currentSummand;
while (!scanner.isAtEnd) {
// Add every summand but the last one
if (currentSummand != nil) {
value = [value decimalNumberByAdding:currentSummand];
}
// Find the operator (+ or -)
NSString *operator;
BOOL found = [scanner scanString:@"+"
intoString:&operator];
if (!found) {
found = [scanner scanString:@"-"
intoString:&operator];
}
if (!found && !scanner.isAtEnd) {
// Two numbers separated by just a space
if (error) {
*error = MPParseError(scanner.scanLocation, @"Missing Operator");
}
return nil;
} else if (!found) {
// Reached end of string
break;
}
currentSummand = [self scanClosedProduct:&productClosed
error:error];
// No number was found
if (currentSummand == nil) {
// The string ends wit + or -
if (scanner.isAtEnd) {
productClosed = NO;
parsedElement.suffixValueExplicit = NO;
NSInteger suffix = [operator isEqualToString:@"+"] ? 1 : -1;
currentSummand = [[NSDecimalNumber alloc] initWithInteger:suffix];
if (error) {
*error = nil;
}
break;
// Something else instead of a number
} else {
if (error) {
*error = MPParseError(scanner.scanLocation, @"Expected Number");
}
return nil;
}
}
// Process the current summand
if ([operator isEqualToString:@"-"]) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:-1]];
}
// The summand ends with a *
if (!productClosed) {
// The string ends with a *
if (scanner.isAtEnd) {
parsedElement.suffixValueExplicit = YES;
break;
// After a * followed something else than a number
} else {
if (error) {
*error = MPParseError(scanner.scanLocation, @"Unexpected Symbol");
}
return nil;
}
}
}
parsedElement.value = value;
parsedElement.suffixOperatorExplicit = !productClosed;
parsedElement.suffixMultiplicator = currentSummand;
parsedElement.suffixIndex = scanner.scanLocation;
return parsedElement;
}
- (NSDecimalNumber *)scanClosedProduct:(BOOL *)closed error:(MPParseError **)error
{
NSDecimal decimal;
BOOL found = [scanner scanDecimal:&decimal];
if (!found) {
if (error != NULL) {
*error = MPParseError(scanner.scanLocation, @"Unexpected Symbol");
}
return nil;
}
NSDecimalNumber *product = [NSDecimalNumber decimalNumberWithDecimal:decimal];
while (true) {
found = [scanner scanString:@"*"
intoString:NULL];
if (found) {
found = [scanner scanDecimal:&decimal];
if (found) {
product = [product decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:decimal]];
} else {
if (closed != NULL) {
*closed = NO;
}
return product;
}
} else {
break;
}
}
if (closed != NULL) {
*closed = YES;
}
return product;
}
@end

View File

@@ -9,7 +9,7 @@
@import Foundation;
#import "NSString+MPExpressionElement.h"
@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator;
@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator, MPParseError;
@protocol MPExpressionElement;
/*!
@@ -242,6 +242,26 @@
- (NSUInteger)indexOfElementAtSymbolLocation:(NSUInteger)location offset:(out NSUInteger *)offset;
/*!
@method locationOfElementAtIndex:
@brief Calculates the location of the element at @c index.
@discussion @c index is an element index. Use this method to convert an
element index into the length reference frame.
If the index exceeds the receiver's number of elements a @c
NSRangeException will be raised.
@param index
The index of the element that is to be converted into the length
reference frame.
@return The number of symbols (in the length reference frame) before the
element at @c index.
*/
- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index;
/*!
@method replaceSymbolsInRange:withElements:
@brief Replaces the elements in the given range with the contents of the
@@ -284,15 +304,40 @@
#pragma mark Evaluating Expressions
- (double)evaluateExpression:(NSError *__autoreleasing *)error;
/*!
@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 errror
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;
// TODO: Private?
/*!
@property evaluator
@brief Returns an object that can evaluate the receiver.
@discussion To just evaluate an expression it is recommended to send it an
@c evaluateWithError: message. You can however use this property
instead if you need more control over the evaluation process.
*/
@property (readonly, nonatomic, strong) MPExpressionEvaluator *evaluator;
#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
// TODO: More notifications
/*!
@method didChangeElementsInRangePath:replacementLength:

View File

@@ -221,6 +221,15 @@
return elementIndex;
}
- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index
{
NSUInteger location = 0;
for (NSUInteger i = 0; i < index; i++) {
location += [self elementAtIndex:i].length;
}
return location;
}
- (void)replaceSymbolsInRange:(NSRange)range
withElements:(NSArray *)elements
{
@@ -268,7 +277,7 @@
return _elements;
}
- (double)evaluateExpression:(NSError *__autoreleasing *)error
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
{
return [self.evaluator evaluateWithError:error];
}

View File

@@ -8,6 +8,8 @@
@import Foundation;
#import "MPParseError.h"
@protocol MPExpressionElement <NSObject, NSCopying, NSCoding>
- (BOOL)isString;
@@ -15,10 +17,6 @@
- (NSUInteger)length;
- (double)doubleValue;
- (float)floatValue;
- (int)intValue;
- (NSInteger)integerValue;
- (long long)longLongValue;
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error;
@end

View File

@@ -8,23 +8,25 @@
#import <Foundation/Foundation.h>
#import "MPExpression.h"
#import "MPElementParser.h"
@class MPExpressionEvaluator, MPExpression, MPParsedElement;
@interface MPExpressionEvaluator : NSObject
// Do not instanciate yourself, use evaluator property of MPExpression instead
- (instancetype)initWithExpression:(MPExpression *)expression;
@property (readonly, nonatomic, strong) MPExpression *expression;
#pragma mark Lexing
- (MPParsedElement *)structuredRepresentationOfElementAtIndex:(NSUInteger)index;
@property (readonly, nonatomic, weak) MPExpression *expression;
#pragma mark Evaluating Expressions
@property (readonly, nonatomic, strong) NSDictionary *variableBindings;
- (void)bindValue:(double)value toVariableName:(NSString *)name;
- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name;
- (void)unbindVariableName:(NSString *)name;
- (double)evaluateWithError:(NSError *__autoreleasing *)error;
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
@property (readonly, nonatomic, strong) NSString *definedVariable;
@end

View File

@@ -9,11 +9,13 @@
#import "MPExpressionEvaluator.h"
#import "MPExpression.h"
#import "MPParsedElement.h"
#import "MPMath.h"
@interface MPExpressionEvaluator ()
@property (readwrite, nonatomic, strong) NSString *definedVariable;
@end
@implementation MPExpressionEvaluator {
NSMutableDictionary *_variableBindings;
MPElementParser *parser;
}
- (id)initWithExpression:(MPExpression *)expression
{
@@ -21,36 +23,176 @@
if (self) {
_expression = expression;
_variableBindings = [[NSMutableDictionary alloc] init];
parser = [[MPElementParser alloc] init];
}
return self;
}
#pragma mark Lexing
- (MPParsedElement *)structuredRepresentationOfElementAtIndex:(NSUInteger)index
{
NSString *string = (NSString *)[self.expression elementAtIndex:index];
YY_BUFFER_STATE buffer = yy_scan_string(string.UTF8String);
yyparse();
yy_delete_buffer(buffer);
return [parseResult copy];
}
#pragma mark Evaluating Expressions
- (NSDictionary *)variableBindings
{
return [_variableBindings copy];
}
- (void)bindValue:(double)value toVariableName:(NSString *)name
- (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name
{
[_variableBindings setObject:@(value)
[_variableBindings setObject:value
forKey:name];
}
- (double)evaluateWithError:(NSError *__autoreleasing *)error
- (void)unbindVariableName:(NSString *)name
{
MPParsedElement *element = [self structuredRepresentationOfElementAtIndex:0];
return element.standaloneValue;
[_variableBindings removeObjectForKey:name];
}
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
{
return [self evaluateVariableDefinition:NO error:error];
}
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error
{
#warning Implement Cache
// Empty Expression
if (self.expression.numberOfElements == 0) {
if (error) {
*error = MPParseError(0, @"Empty Expression");
}
return nil;
}
// Expression of just one element
if (self.expression.numberOfElements == 1) {
id<MPExpressionElement> singleElement = [self.expression elementAtIndex:0];
if ([singleElement isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)singleElement
error:error];
if ([parseResult isValidStandaloneElement:error]) {
if (flag && parseResult.definedVariable) {
self.definedVariable = parseResult.definedVariable;
} else if (flag) {
if (error) {
*error = MPParseError(0, @"Expected Variable Definition");
}
return nil;
} else if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(0, @"Unexpected Variable Definition");
}
return nil;
}
return parseResult.standaloneValue;
} else {
return nil;
}
} else {
return [singleElement evaluate:error];
}
}
// Expression with any number of elements
NSDecimalNumber *value = [NSDecimalNumber zero];
NSDecimalNumber *currentSummand = nil;
// Process the first element
id<MPExpressionElement> firstElement = [self.expression elementAtIndex:0];
if ([firstElement isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)firstElement
error:error];
if (parseResult && [parseResult isValidElementAtBeginning:error]) {
if (flag && parseResult.definedVariable) {
self.definedVariable = parseResult.definedVariable;
} else if (flag) {
if (error) {
*error = MPParseError(0, @"Expected Variable Definition");
}
return nil;
} else if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(0, @"Unexpected Variable Definition");
}
return nil;
}
if ([parseResult isFactor]) {
currentSummand = parseResult.value;
} else {
value = [parseResult valueAtBeginning];
currentSummand = parseResult.suffixMultiplicator;
}
}
} else {
currentSummand = [firstElement evaluate:error];
}
if (!currentSummand) {
return nil;
}
// Process the elements between the first and last element
for (NSUInteger index = 1; index < self.expression.numberOfElements-1; index++) {
id<MPExpressionElement> element = [self.expression elementAtIndex:index];
if ([element isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)element
error:error];
if (parseResult) {
if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(index, @"Unexpected Variable Definition");
}
return nil;
}
if ([parseResult isFactor]) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
} else {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
currentSummand = parseResult.suffixMultiplicator;
}
} else {
return nil;
}
} else {
NSDecimalNumber *result = [element evaluate:error];
if (result) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
} else {
return nil;
}
}
}
// Process the last element
id<MPExpressionElement> lastElement = [self.expression elementAtIndex:self.expression.numberOfElements-1];
if ([lastElement isString]) {
MPParsedElement *parseResult = [parser parseElement:(NSString *)lastElement
error:error];
if (parseResult && [parseResult isValidElementAtEnd:error]) {
if (parseResult.definedVariable) {
if (error) {
*error = MPParseError(0, @"Unexpected Variable Definition");
}
return nil;
}
if ([parseResult isFactor]) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value];
} else {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator];
value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value];
currentSummand = parseResult.suffixMultiplicator;
}
} else {
return nil;
}
} else {
NSDecimalNumber *result = [lastElement evaluate:error];
if (result) {
currentSummand = [currentSummand decimalNumberByMultiplyingBy:result];
} else {
return nil;
}
}
value = [value decimalNumberByAdding:currentSummand];
return value;
}
@end

View File

@@ -8,6 +8,7 @@
@import Foundation;
#import "MPExpressionElement.h"
#import "MPParseError.h"
@class MPFunction, MPExpression, MPRangePath;
@@ -37,7 +38,7 @@
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
#pragma mark Evaluating Functions
- (double)evaluateFunction:(NSError *__autoreleasing *)error;
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error;
#pragma mark Messages
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath

View File

@@ -82,9 +82,9 @@
}
#pragma mark Evaluating Functions
- (double)evaluateFunction:(NSError *__autoreleasing *)error
- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error
{
return 0;
return [NSDecimalNumber zero];
}
#pragma mark Notifications
@@ -182,24 +182,4 @@
return 1;
}
- (float)floatValue
{
return (float)[self doubleValue];
}
- (int)intValue
{
return (int)[self doubleValue];
}
- (NSInteger)integerValue
{
return (NSInteger)[self doubleValue];
}
- (long long)longLongValue
{
return (long long)[self doubleValue];
}
@end

21
MathPad/MPParseError.h Normal file
View File

@@ -0,0 +1,21 @@
//
// MPParseError.h
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#define MPParseError(index,key) [[MPParseError alloc] initWithErrorIndex:index errorMessageKey:key]
@interface MPParseError : NSObject
- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex
errorMessageKey:(NSString *)errorMessageKey;
@property (nonatomic) NSUInteger errorIndex;
@property (nonatomic) NSString *localizedErrorMessage;
@end

28
MathPad/MPParseError.m Normal file
View File

@@ -0,0 +1,28 @@
//
// MPParseError.m
// MathPad
//
// Created by Kim Wittenburg on 06.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPParseError.h"
@implementation MPParseError
- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex errorMessageKey:(NSString *)errorMessageKey
{
self = [super init];
if (self) {
_errorIndex = errorIndex;
_localizedErrorMessage = NSLocalizedString(errorMessageKey, nil);
}
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"MPParseError<at=%ld message=\"%@\">", self.errorIndex, self.localizedErrorMessage];
}
@end

View File

@@ -8,32 +8,37 @@
#import <Foundation/Foundation.h>
// DEBUG STUFF
// Simple Operator: +/-/*
// Simple Factor: * 5.4 * / * 5,4 / 5,4 * / 5,4
// Independant Math: -1+2-3
#import "MPParseError.h"
@interface MPParsedElement : NSObject <NSCopying>
@property (nonatomic, copy) NSString *definedVariable;
@property (nonatomic) NSUInteger afterVariableDefinitionIndex; // Only set if defineVariable != nil
@property (nonatomic) BOOL isFactor;
@property (nonatomic) double factor;
@property (nonatomic, strong) NSMutableArray *summands;
// If isFactor is YES this is the factor otherwise it is the independant summant
// of the element (may be 0).
@property (nonatomic) NSDecimalNumber *value;
@property (nonatomic) double prefixMultiplicator;
@property (nonatomic) BOOL hasPrefixMultiplicator;
@property (nonatomic, getter = isPrefixOperatorExplicit) BOOL prefixOperatorExplicit;
@property (nonatomic) double suffixMultiplicator;
@property (nonatomic) BOOL hasSuffixMultiplicator;
@property (nonatomic, getter = isPrefixValueExplicit) BOOL prefixValueExplicit;
@property (nonatomic) NSDecimalNumber *prefixMultiplicator;
// No error checking done. The string parsed may for example end with an operator
- (double)valueAtBeginning;
- (double)valueAtEnd;
- (double)standaloneValue;
@property (nonatomic, getter = isSuffixOperatorExplicit) BOOL suffixOperatorExplicit;
@property (nonatomic, getter = isSuffixValueExplicit) BOOL suffixValueExplicit;
@property (nonatomic) NSDecimalNumber *suffixMultiplicator;
@property (nonatomic) NSUInteger suffixIndex;
// No error checking done
- (NSDecimalNumber *)valueAtBeginning;
- (NSDecimalNumber *)valueAtEnd;
- (NSDecimalNumber *)standaloneValue;
// For error checking
- (BOOL)isValidElementAtBeginning;
- (BOOL)isValidElementInBetween;
- (BOOL)isValidElementAtEnd;
- (BOOL)isValidElementAtBeginning:(MPParseError **)error;
- (BOOL)isValidStandaloneElement:(MPParseError **)error;
- (BOOL)isValidElementAtEnd:(MPParseError **)error;
- (BOOL)isValidVariableDefinition:(MPParseError **)error;
@end

View File

@@ -14,46 +14,109 @@
{
self = [super init];
if (self) {
_summands = [[NSMutableArray alloc] init];
}
return self;
}
- (double)standaloneValue
- (NSDecimalNumber *)valueAtBeginning
{
NSDecimalNumber *value = self.value;
if (self.prefixValueExplicit) {
value = [value decimalNumberByAdding:self.prefixMultiplicator];
}
return value;
}
- (NSDecimalNumber *)valueAtEnd
{
NSDecimalNumber *value = self.value;
if (self.suffixValueExplicit) {
value = [value decimalNumberByAdding:self.suffixMultiplicator];
}
return value;
}
- (NSDecimalNumber *)standaloneValue
{
if (self.isFactor) {
return self.factor;
return self.value;
}
return self.prefixMultiplicator + [[self.summands valueForKeyPath:@"@sum.self"] doubleValue];
NSDecimalNumber *value = self.value;
if (self.prefixValueExplicit) {
value = [value decimalNumberByAdding:self.prefixMultiplicator];
}
if (self.suffixValueExplicit) {
value = [value decimalNumberByAdding:self.suffixMultiplicator];
}
return value;
}
- (BOOL)isValidElementAtBeginning
- (BOOL)isValidElementAtBeginning:(MPParseError *__autoreleasing *)error
{
if (self.prefixOperatorExplicit) {
if (error) {
*error = MPParseError(0, @"Expected Number");
}
return NO;
}
- (BOOL)isValidElementInBetween
{
}
return YES;
}
- (BOOL)isValidElementAtEnd
- (BOOL)isValidStandaloneElement:(MPParseError *__autoreleasing *)error
{
return NO;
return [self isValidElementAtBeginning:error] && [self isValidElementAtEnd:error];
}
#pragma mark NSCopying
- (BOOL)isValidElementAtEnd:(MPParseError *__autoreleasing *)error
{
if (self.suffixOperatorExplicit) {
if (error) {
*error = MPParseError(self.suffixIndex, @"Expected Number");
}
return NO;
}
return YES;
}
- (BOOL)isValidVariableDefinition:(MPParseError *__autoreleasing *)error
{
if (self.definedVariable == nil) {
if (error) {
*error = MPParseError(0, @"Expected Variable Definition");
}
return NO;
}
return [self isValidElementAtBeginning:error];
}
#pragma mark - NSObject Overrides
- (NSString *)description
{
NSMutableString *description = [[NSMutableString alloc] initWithString:@"MPParsedElement<"];
if (self.isFactor) {
[description appendFormat:@"factor=%@%@%@", self.prefixOperatorExplicit?@"*":@"", self.value, self.suffixOperatorExplicit?@"*":@""];
} else {
[description appendFormat:@"prefix=%@%@ prefixValueExplicit=%@", self.prefixOperatorExplicit?@"*":@"", self.prefixMultiplicator, self.prefixValueExplicit?@"YES":@"NO"];
[description appendFormat:@" suffix=%@%@ suffixValueExplicit=%@", self.suffixMultiplicator, self.suffixOperatorExplicit?@"*":@"", self.suffixValueExplicit?@"YES":@"NO"];
[description appendFormat:@" value=%@", self.value];
}
[description appendString:@">"];
return [description copy];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
MPParsedElement *copy = [[MPParsedElement allocWithZone:zone] init];
copy.isFactor = self.isFactor;
copy.factor = self.factor;
copy.summands = self.summands.mutableCopy;
copy.prefixMultiplicator = self.prefixMultiplicator;
copy.hasPrefixMultiplicator = self.hasPrefixMultiplicator;
copy.value = self.value;
copy.prefixOperatorExplicit = self.prefixOperatorExplicit;
copy.prefixValueExplicit = self.prefixValueExplicit;
copy.prefixMultiplicator = self.prefixMultiplicator;
copy.suffixOperatorExplicit = self.suffixOperatorExplicit;
copy.suffixValueExplicit = self.suffixValueExplicit;
copy.suffixMultiplicator = self.suffixMultiplicator;
copy.hasSuffixMultiplicator = self.hasSuffixMultiplicator;
return copy;
}