Improved Evaluation
This commit is contained in:
@@ -15,9 +15,10 @@
|
|||||||
- (void)push;
|
- (void)push;
|
||||||
- (void)pop;
|
- (void)pop;
|
||||||
|
|
||||||
- (void)bindValue:(NSDecimalNumber *)value toName:(NSString *)variableName;
|
- (void)defineVariable:(NSString *)variable withValue:(id)value;
|
||||||
- (void)unbindVariableName:(NSString *)variableName;
|
- (void)undefineVariable:(NSString *)variable;
|
||||||
|
- (BOOL)isVariableDefined:(NSString *)variable;
|
||||||
|
|
||||||
- (NSDecimalNumber *)valueForVariableName:(NSString *)variableName;
|
- (NSDecimalNumber *)valueForVariable:(NSString *)variable;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -45,22 +45,27 @@ static MPEvaluationContext *sharedContext;
|
|||||||
[self.stack removeLastObject];
|
[self.stack removeLastObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)bindValue:(NSDecimalNumber *)value toName:(NSString *)variableName
|
- (void)defineVariable:(NSString *)variable withValue:(id)value
|
||||||
{
|
{
|
||||||
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
||||||
currentBindings[variableName] = value;
|
currentBindings[variable] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unbindVariableName:(NSString *)variableName
|
- (void)undefineVariable:(NSString *)variable
|
||||||
{
|
{
|
||||||
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
||||||
[currentBindings removeObjectForKey:variableName];
|
[currentBindings removeObjectForKey:variable];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDecimalNumber *)valueForVariableName:(NSString *)variableName
|
- (NSDecimalNumber *)valueForVariable:(NSString *)variable
|
||||||
{
|
{
|
||||||
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
||||||
return currentBindings[variableName];
|
return currentBindings[variable];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isVariableDefined:(NSString *)variable
|
||||||
|
{
|
||||||
|
return [self valueForVariable:variable] != nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -258,7 +258,9 @@
|
|||||||
|
|
||||||
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return [self.evaluator evaluateWithError:error];
|
MPTerm *term = [self.evaluator parseExpectingVariable:NO
|
||||||
|
error:error];
|
||||||
|
return [term evaluate];
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize evaluator = _evaluator;
|
@synthesize evaluator = _evaluator;
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
#import "MPElementParser.h"
|
#import "MPExpressionTokenizer.h"
|
||||||
|
#import "MPTerm.h"
|
||||||
|
|
||||||
@class MPExpressionEvaluator, MPExpression, MPParsedElementOld;
|
@class MPExpressionEvaluator, MPExpression, MPParsedElementOld;
|
||||||
|
|
||||||
@@ -18,10 +19,14 @@
|
|||||||
- (instancetype)initWithExpression:(MPExpression *)expression;
|
- (instancetype)initWithExpression:(MPExpression *)expression;
|
||||||
@property (readonly, nonatomic, weak) MPExpression *expression;
|
@property (readonly, nonatomic, weak) MPExpression *expression;
|
||||||
|
|
||||||
#pragma mark Evaluating Expressions
|
@property (nonatomic, strong) MPExpressionTokenizer *lexer;
|
||||||
@property (readonly, nonatomic, strong) NSString *definedVariable;
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
|
- (void)expressionDidChangeInRange:(NSRange)range
|
||||||
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
|
replacementLength:(NSUInteger)replacementLength;
|
||||||
|
|
||||||
|
#pragma mark Evaluating Expressions
|
||||||
|
@property (readonly, nonatomic, copy) NSString *definedVariable;
|
||||||
|
|
||||||
|
- (MPTerm *)parseExpectingVariable:(BOOL)flag error:(MPParseError *__autoreleasing *)error;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,105 +9,236 @@
|
|||||||
#import "MPExpressionEvaluator.h"
|
#import "MPExpressionEvaluator.h"
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
#import "MPParsedFactor.h"
|
|
||||||
#import "MPFunction+MPParsedFactor.h"
|
|
||||||
#import "MPMathRules.h"
|
#import "MPMathRules.h"
|
||||||
|
#import "MPToken.h"
|
||||||
|
#import "MPFunction+MPToken.h"
|
||||||
|
#import "MPTokenStream.h"
|
||||||
|
|
||||||
|
#import "MPEvaluationContext.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionEvaluator ()
|
@interface MPExpressionEvaluator ()
|
||||||
@property (readwrite, nonatomic, strong) NSString *definedVariable;
|
@property (readwrite, nonatomic, copy) NSString *definedVariable;
|
||||||
|
@property (nonatomic, strong) NSArray *tokens;
|
||||||
|
|
||||||
|
- (void)setError:(MPParseError *)error;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPExpressionEvaluator {
|
@implementation MPExpressionEvaluator {
|
||||||
MPElementParser *parser;
|
MPParseError *__autoreleasing *_error;
|
||||||
|
MPTokenStream *tokenStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithExpression:(MPExpression *)expression
|
- (id)initWithExpression:(MPExpression *)expression
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_expression = expression;
|
_expression = expression;
|
||||||
parser = [[MPElementParser alloc] init];
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Evaluating Expressions
|
@synthesize lexer = _lexer;
|
||||||
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
|
||||||
|
- (void)setLexer:(MPExpressionTokenizer *)lexer
|
||||||
{
|
{
|
||||||
return [self evaluateVariableDefinition:NO error:error];
|
_lexer = lexer;
|
||||||
|
self.tokens = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error
|
- (MPExpressionTokenizer *)lexer
|
||||||
{
|
{
|
||||||
// Empty Expression
|
if (!_lexer) {
|
||||||
if (self.expression.numberOfElements == 0) {
|
_lexer = [[MPExpressionTokenizer alloc] init];
|
||||||
*error = MPParseError(NSMakeRange(0, 0), @"Expected Expression");
|
}
|
||||||
|
return _lexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)tokens
|
||||||
|
{
|
||||||
|
if (!_tokens) {
|
||||||
|
_tokens = [self.lexer tokenizeExpression:self.expression];
|
||||||
|
}
|
||||||
|
return _tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)expressionDidChangeInRange:(NSRange)range
|
||||||
|
replacementLength:(NSUInteger)replacementLength
|
||||||
|
{
|
||||||
|
self.tokens = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark Evaluating Expressions
|
||||||
|
- (void)setError:(MPParseError *)error
|
||||||
|
{
|
||||||
|
if (_error) {
|
||||||
|
error.pathToExpression = self.expression.indexPath;
|
||||||
|
*_error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPTerm *)parseExpectingVariable:(BOOL)flag
|
||||||
|
error:(MPParseError *__autoreleasing *)error
|
||||||
|
{
|
||||||
|
_error = error;
|
||||||
|
tokenStream = [[MPTokenStream alloc] initWithTokens:self.tokens];
|
||||||
|
if (!tokenStream.hasMoreTokens) {
|
||||||
|
self.error = MPParseError(NSMakeRange(0, 0), @"Empty Expression");
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableArray *products = [[NSMutableArray alloc] init];
|
|
||||||
MPParsedProduct *currentProduct = nil;
|
|
||||||
|
|
||||||
for (NSUInteger elementIndex = 0; elementIndex < self.expression.numberOfElements; elementIndex++) {
|
// Variable Definition
|
||||||
|
if (flag) {
|
||||||
|
MPToken *variableToken = [tokenStream nextToken];
|
||||||
|
MPToken *equalsToken = [tokenStream nextTokenOfType:MPEqualsToken];
|
||||||
|
|
||||||
id<MPExpressionElement> element = [self.expression elementAtIndex:elementIndex];
|
if (variableToken.tokenType != MPVariableToken) {
|
||||||
|
self.error = MPParseError(variableToken.range, @"Expected Variable Definition.");
|
||||||
if ([element isString]) {
|
return nil;
|
||||||
id<MPParsedFactor> nextFactor = nil;
|
}
|
||||||
if (elementIndex < self.expression.numberOfElements - 1) {
|
if (equalsToken.tokenType != MPEqualsToken) {
|
||||||
MPFunction *nextFunction = (MPFunction *)[self.expression elementAtIndex:elementIndex+1];
|
self.error = MPParseError(equalsToken.range, @"Expected \"=\".");
|
||||||
nextFactor = nextFunction;
|
return nil;
|
||||||
}
|
}
|
||||||
|
self.definedVariable = variableToken.stringValue;
|
||||||
NSArray *newProducts;
|
}
|
||||||
if (elementIndex == 0 && flag) {
|
|
||||||
NSString *definedVariable;
|
BOOL isAtBeginning = YES;
|
||||||
newProducts = [parser parseElement:(NSString *)element
|
NSMutableArray *summands = [[NSMutableArray alloc] init];
|
||||||
previousProduct:currentProduct
|
NSMutableArray *currentSummand = [[NSMutableArray alloc] init];
|
||||||
nextFactor:nextFactor
|
|
||||||
definesVariable:YES
|
while (tokenStream.hasMoreTokens) {
|
||||||
definedVariable:&definedVariable
|
MPToken *multiplicationToken = [tokenStream nextTokenOfType:MPMultiplicationSymbolToken];
|
||||||
error:error];
|
MPToken *operatorToken = [tokenStream nextTokenOfType:MPOperatorListToken];
|
||||||
self.definedVariable = definedVariable;
|
NSRange valueRange;
|
||||||
} else {
|
MPTerm *value = [self nextValue:&valueRange];
|
||||||
newProducts = [parser parseElement:(NSString *)element
|
|
||||||
previousProduct:currentProduct
|
if (multiplicationToken) {
|
||||||
nextFactor:nextFactor
|
if (isAtBeginning) {
|
||||||
error:error];
|
self.error = MPParseError(multiplicationToken.range, @"Unexpected Symbol. Expected Number.");
|
||||||
}
|
return nil;
|
||||||
if (!newProducts) {
|
}
|
||||||
return nil;
|
if (operatorToken) {
|
||||||
}
|
if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) {
|
||||||
|
self.error = MPParseError(operatorToken.range, @"Too many operators in multiplication");
|
||||||
for (NSUInteger productIndex = 0; productIndex < newProducts.count-1; productIndex++) {
|
return nil;
|
||||||
[products addObject:newProducts[productIndex]];
|
}
|
||||||
}
|
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
|
||||||
currentProduct = newProducts.lastObject;
|
}
|
||||||
|
if (value) {
|
||||||
elementIndex++;
|
[currentSummand addObject:value];
|
||||||
} else {
|
} else {
|
||||||
if (!currentProduct) {
|
return nil;
|
||||||
currentProduct = [[MPParsedProduct alloc] init];
|
}
|
||||||
} else if ([MPMathRules sharedRules].allowsImplicitMultiplication) {
|
} else {
|
||||||
[currentProduct addFactor:(MPFunction *)element];
|
if (operatorToken) {
|
||||||
} else {
|
if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLength]) {
|
||||||
*error = MPParseError(NSMakeRange(((MPFunction *)element).range.location, 0), @"Implicit Multiplication not allowed");
|
self.error = MPParseError(operatorToken.range, @"Too many operators.");
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
}
|
[summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
|
||||||
}
|
currentSummand = [[NSMutableArray alloc] init];
|
||||||
|
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
|
||||||
[products addObject:currentProduct];
|
if (value) {
|
||||||
|
[currentSummand addObject:value];
|
||||||
NSDecimalNumber *value = [NSDecimalNumber zero];
|
} else {
|
||||||
for (MPParsedProduct *product in products) {
|
return nil;
|
||||||
NSDecimalNumber *productValue = [product evaluateWithError:error];
|
}
|
||||||
if (!productValue) {
|
} else if (!value) {
|
||||||
|
return nil;
|
||||||
|
} else if (isAtBeginning || [[MPMathRules sharedRules] allowsImplicitMultiplication]) {
|
||||||
|
[currentSummand addObject:value];
|
||||||
|
} else {
|
||||||
|
self.error = MPParseError(NSMakeRange(valueRange.location, 0), @"Implicit Multiplication not allowed here");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isAtBeginning = NO;
|
||||||
|
}
|
||||||
|
[summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
|
||||||
|
return [[MPTerm alloc] initWithSummands:summands.copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPTerm *)nextValue:(NSRangePointer)range
|
||||||
|
{
|
||||||
|
MPToken *token = [tokenStream nextToken];
|
||||||
|
if (range) {
|
||||||
|
*range = token.range;
|
||||||
|
}
|
||||||
|
switch (token.tokenType) {
|
||||||
|
case MPNumberToken:
|
||||||
|
{
|
||||||
|
return [[MPTerm alloc] initWithNumber:token.number];
|
||||||
|
}
|
||||||
|
|
||||||
|
case MPVariableToken:
|
||||||
|
{
|
||||||
|
if(![[MPEvaluationContext sharedContext] isVariableDefined:token.variable]) {
|
||||||
|
self.error = MPParseError(token.range, @"Undefined Variable");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [[MPTerm alloc] initWithVariable:token.variable];
|
||||||
|
}
|
||||||
|
|
||||||
|
case MPGenericFunctionToken:
|
||||||
|
{
|
||||||
|
return [((MPFunction *)token) parseWithError:_error];
|
||||||
|
}
|
||||||
|
|
||||||
|
case MPSinToken:
|
||||||
|
{
|
||||||
|
NSRange sinTermRange;
|
||||||
|
MPTerm *sinTerm = [self nextValue:&sinTermRange];
|
||||||
|
if (!sinTerm) {
|
||||||
|
if (range) {
|
||||||
|
*range = NSUnionRange(token.range, sinTermRange);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [[MPTerm alloc] initWithSinOfTerm:sinTerm];
|
||||||
|
}
|
||||||
|
|
||||||
|
case MPCosToken:
|
||||||
|
{
|
||||||
|
NSRange cosTermRange;
|
||||||
|
MPTerm *cosTerm = [self nextValue:&cosTermRange];
|
||||||
|
if (!cosTerm) {
|
||||||
|
if (range) {
|
||||||
|
*range = NSUnionRange(token.range, cosTermRange);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [[MPTerm alloc] initWithSinOfTerm:cosTerm];
|
||||||
|
}
|
||||||
|
|
||||||
|
case MPTanToken:
|
||||||
|
{
|
||||||
|
NSRange tanTermRange;
|
||||||
|
MPTerm *tanTerm = [self nextValue:&tanTermRange];
|
||||||
|
if (!tanTerm) {
|
||||||
|
if (range) {
|
||||||
|
*range = NSUnionRange(token.range, tanTermRange);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [[MPTerm alloc] initWithTanOfTerm:tanTerm];
|
||||||
|
}
|
||||||
|
|
||||||
|
case MPEOFToken:
|
||||||
|
{
|
||||||
|
self.error = MPParseError(token.range, @"Unexpected End. Expected Number.");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
self.error = MPParseError(token.range, @"Unexpected Symbol. Expected Number.");
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
value = [value decimalNumberByAdding:productValue];
|
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
16
MathPad/MPExpressionTokenizer.h
Normal file
16
MathPad/MPExpressionTokenizer.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MPExpressionLexer.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 19.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MPExpression.h"
|
||||||
|
|
||||||
|
@interface MPExpressionTokenizer : NSObject
|
||||||
|
|
||||||
|
- (NSArray *)tokenizeExpression:(MPExpression *)expression; // Returns MPToken's
|
||||||
|
|
||||||
|
@end
|
||||||
115
MathPad/MPExpressionTokenizer.m
Normal file
115
MathPad/MPExpressionTokenizer.m
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// MPExpressionLexer.m
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 19.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPExpressionTokenizer.h"
|
||||||
|
#import "MPToken.h"
|
||||||
|
#import "MPFunction+MPToken.h"
|
||||||
|
|
||||||
|
#import "NSRegularExpression+MPParsingAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MPRangeExists(range) (range.location != NSNotFound)
|
||||||
|
|
||||||
|
|
||||||
|
@implementation MPExpressionTokenizer
|
||||||
|
|
||||||
|
- (NSArray *)tokenizeExpression:(MPExpression *)expression
|
||||||
|
{
|
||||||
|
NSMutableArray *tokens = [[NSMutableArray alloc] init];
|
||||||
|
for (NSUInteger index = 0; index < expression.numberOfElements; index++) {
|
||||||
|
id <MPExpressionElement> element = [expression elementAtIndex:index];
|
||||||
|
if ([element isFunction]) {
|
||||||
|
[tokens addObject:element];
|
||||||
|
} else {
|
||||||
|
[tokens addObjectsFromArray:[self tokenizeElement:(NSString *)element]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)tokenizeElement:(NSString *)element
|
||||||
|
{
|
||||||
|
NSUInteger lexLocation = 0;
|
||||||
|
|
||||||
|
NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]];
|
||||||
|
NSString *regexStringFormat = @"\\A(?:"
|
||||||
|
@"(\\*)|"
|
||||||
|
@"([+-](?:\\s*[+-])*)|"
|
||||||
|
@"((?:\\d+(?:%@\\d+)?)|(?:\\s%@\\d+))|"
|
||||||
|
@"(sin)|"
|
||||||
|
@"(cos)|"
|
||||||
|
@"(tan)|"
|
||||||
|
@"([A-Za-z])|"
|
||||||
|
@"(=)|"
|
||||||
|
@"(\\s)"
|
||||||
|
@")";
|
||||||
|
NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator];
|
||||||
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString
|
||||||
|
options:0
|
||||||
|
error:NULL];
|
||||||
|
NSMutableArray *tokens = [[NSMutableArray alloc] init];
|
||||||
|
while (lexLocation < element.length) {
|
||||||
|
NSTextCheckingResult *match = [regex firstMatchInString:element fromIndex:lexLocation];
|
||||||
|
|
||||||
|
NSRange range = NSMakeRange(lexLocation, 1);
|
||||||
|
MPTokenType tokenType = MPUnidentifiedToken;
|
||||||
|
if (match) {
|
||||||
|
NSRange multiplicationSymbolRange = [match rangeAtIndex:1];
|
||||||
|
NSRange operatorRange = [match rangeAtIndex:2];
|
||||||
|
NSRange numberRange = [match rangeAtIndex:3];
|
||||||
|
NSRange sinRange = [match rangeAtIndex:4];
|
||||||
|
NSRange cosRange = [match rangeAtIndex:5];
|
||||||
|
NSRange tanRange = [match rangeAtIndex:6];
|
||||||
|
NSRange variableRange = [match rangeAtIndex:7];
|
||||||
|
NSRange equalsRange = [match rangeAtIndex:8];
|
||||||
|
NSRange whitespaceRange = [match rangeAtIndex:9];
|
||||||
|
|
||||||
|
if (MPRangeExists(multiplicationSymbolRange)) {
|
||||||
|
range = multiplicationSymbolRange;
|
||||||
|
tokenType = MPMultiplicationSymbolToken;
|
||||||
|
} else if (MPRangeExists(operatorRange)) {
|
||||||
|
range = operatorRange;
|
||||||
|
tokenType = MPOperatorListToken;
|
||||||
|
} else if (MPRangeExists(numberRange)) {
|
||||||
|
range = numberRange;
|
||||||
|
tokenType = MPNumberToken;
|
||||||
|
} else if (MPRangeExists(sinRange)) {
|
||||||
|
range = sinRange;
|
||||||
|
tokenType = MPSinToken;
|
||||||
|
} else if (MPRangeExists(cosRange)) {
|
||||||
|
range = cosRange;
|
||||||
|
tokenType = MPCosToken;
|
||||||
|
} else if (MPRangeExists(tanRange)) {
|
||||||
|
range = tanRange;
|
||||||
|
tokenType = MPTanToken;
|
||||||
|
} else if (MPRangeExists(variableRange)) {
|
||||||
|
range = variableRange;
|
||||||
|
tokenType = MPVariableToken;
|
||||||
|
} else if (MPRangeExists(equalsRange)) {
|
||||||
|
range = equalsRange;
|
||||||
|
tokenType = MPEqualsToken;
|
||||||
|
} else if (MPRangeExists(whitespaceRange)) {
|
||||||
|
range = whitespaceRange;
|
||||||
|
tokenType = MPWhitespaceToken;
|
||||||
|
} else {
|
||||||
|
// Should not get here
|
||||||
|
range = NSMakeRange(lexLocation, 1);
|
||||||
|
tokenType = MPUnidentifiedToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lexLocation = NSMaxRange(range);
|
||||||
|
[tokens addObject:[[MPToken alloc] initWithTokenType:tokenType
|
||||||
|
range:range
|
||||||
|
inString:element]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
14
MathPad/MPFunction+MPToken.h
Normal file
14
MathPad/MPFunction+MPToken.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// MPFunction+MPToken.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 19.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <MathKit/MathKit.h>
|
||||||
|
#import "MPToken.h"
|
||||||
|
|
||||||
|
@interface MPFunction (MPToken) <MPToken>
|
||||||
|
|
||||||
|
@end
|
||||||
34
MathPad/MPFunction+MPToken.m
Normal file
34
MathPad/MPFunction+MPToken.m
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// MPFunction+MPToken.m
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 19.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPFunction+MPToken.h"
|
||||||
|
|
||||||
|
@implementation MPFunction (MPToken)
|
||||||
|
|
||||||
|
- (MPTokenType)tokenType
|
||||||
|
{
|
||||||
|
return MPGenericFunctionToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRange)range
|
||||||
|
{
|
||||||
|
NSUInteger selfIndex = [self.parent indexOfElement:self];
|
||||||
|
return NSMakeRange([self.parent locationOfElementAtIndex:selfIndex], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)exists
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)stringValue
|
||||||
|
{
|
||||||
|
return [self description];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -9,11 +9,14 @@
|
|||||||
@import Foundation;
|
@import Foundation;
|
||||||
#import "MPExpressionElement.h"
|
#import "MPExpressionElement.h"
|
||||||
#import "MPParseError.h"
|
#import "MPParseError.h"
|
||||||
|
#import "MPTerm.h"
|
||||||
|
|
||||||
@class MPFunction, MPExpression, MPRangePath;
|
@class MPFunction, MPExpression, MPRangePath;
|
||||||
|
|
||||||
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
|
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
|
||||||
|
|
||||||
|
+ (NSString *)localizedFunctionName; // Override
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
- (instancetype)init;
|
- (instancetype)init;
|
||||||
@@ -38,7 +41,8 @@
|
|||||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
#pragma mark Evaluating Functions
|
#pragma mark Evaluating Functions
|
||||||
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
|
|
||||||
|
- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error; // Override
|
||||||
|
|
||||||
#pragma mark Messages
|
#pragma mark Messages
|
||||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||||
@@ -52,4 +56,6 @@
|
|||||||
- (NSString *)description; // Should be overridden
|
- (NSString *)description; // Should be overridden
|
||||||
- (NSUInteger)hash;// Override
|
- (NSUInteger)hash;// Override
|
||||||
|
|
||||||
|
- (id)copyWithZone:(NSZone *)zone; // Override
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
@implementation MPFunction
|
@implementation MPFunction
|
||||||
|
|
||||||
|
+ (NSString *)localizedFunctionName
|
||||||
|
{
|
||||||
|
return NSLocalizedString(@"Function", @"Name of Generic Function.");
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
@@ -82,9 +87,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Evaluating Functions
|
#pragma mark Evaluating Functions
|
||||||
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
|
- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return [NSDecimalNumber zero];
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Notifications
|
#pragma mark Notifications
|
||||||
|
|||||||
@@ -6,8 +6,12 @@
|
|||||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// TODO: Rename to MPSyntaxError
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class MPRangePath;
|
||||||
|
|
||||||
#define MPParseError(range,key) [[MPParseError alloc] initWithErrorRange:range errorMessageKey:key]
|
#define MPParseError(range,key) [[MPParseError alloc] initWithErrorRange:range errorMessageKey:key]
|
||||||
|
|
||||||
@interface MPParseError : NSObject
|
@interface MPParseError : NSObject
|
||||||
@@ -23,4 +27,6 @@
|
|||||||
@property (nonatomic) NSRange errorRange;
|
@property (nonatomic) NSRange errorRange;
|
||||||
@property (nonatomic, copy) NSString *localizedErrorMessage;
|
@property (nonatomic, copy) NSString *localizedErrorMessage;
|
||||||
|
|
||||||
|
- (MPRangePath *)rangePath;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#import "MPParseError.h"
|
#import "MPParseError.h"
|
||||||
|
|
||||||
|
#import "MPRangePath.h"
|
||||||
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
@implementation MPParseError
|
@implementation MPParseError
|
||||||
|
|
||||||
- (instancetype)initWithErrorRange:(NSRange)errorRange errorMessageKey:(NSString *)key
|
- (instancetype)initWithErrorRange:(NSRange)errorRange errorMessageKey:(NSString *)key
|
||||||
@@ -30,6 +33,14 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (MPRangePath *)rangePath
|
||||||
|
{
|
||||||
|
NSIndexPath *location = self.pathToExpression;
|
||||||
|
location = [location indexPathByPreceedingIndex:self.errorRange.location];
|
||||||
|
MPRangePath *rangePath = [MPRangePath rangePathWithLocation:location length:self.errorRange.length];
|
||||||
|
return rangePath;
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description
|
||||||
{
|
{
|
||||||
return [NSString stringWithFormat:@"MPParseError<at=(%ld, %ld) message=\"%@\">", self.errorRange.location, self.errorRange.length, self.localizedErrorMessage];
|
return [NSString stringWithFormat:@"MPParseError<at=(%ld, %ld) message=\"%@\">", self.errorRange.location, self.errorRange.length, self.localizedErrorMessage];
|
||||||
|
|||||||
26
MathPad/MPTerm.h
Normal file
26
MathPad/MPTerm.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// MPTerm.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 27.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
@import Foundation;
|
||||||
|
|
||||||
|
@interface MPTerm : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithBlock:(NSDecimalNumber *(^)())block; // Designated Initializer
|
||||||
|
|
||||||
|
- (instancetype)initWithNumber:(NSDecimalNumber *)number;
|
||||||
|
- (instancetype)initWithSummands:(NSArray *)summands; // array of MPTerms
|
||||||
|
- (instancetype)initWithFactors:(NSArray *)factors; // array of MPTerms
|
||||||
|
- (instancetype)initWithVariable:(NSString *)variable;
|
||||||
|
|
||||||
|
- (instancetype)initWithSinOfTerm:(MPTerm *)term;
|
||||||
|
- (instancetype)initWithCosOfTerm:(MPTerm *)term;
|
||||||
|
- (instancetype)initWithTanOfTerm:(MPTerm *)term;
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)evaluate;
|
||||||
|
|
||||||
|
@end
|
||||||
94
MathPad/MPTerm.m
Normal file
94
MathPad/MPTerm.m
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// MPTerm.m
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 27.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPTerm.h"
|
||||||
|
#import "MPEvaluationContext.h"
|
||||||
|
|
||||||
|
@interface MPTerm ()
|
||||||
|
|
||||||
|
@property (nonatomic, copy) NSDecimalNumber *(^block)();
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MPTerm
|
||||||
|
|
||||||
|
- (instancetype)initWithBlock:(NSDecimalNumber *(^)())block
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.block = block;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithNumber:(NSDecimalNumber *)number
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
return number;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithSummands:(NSArray *)summands
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
NSDecimalNumber *value = [NSDecimalNumber zero];
|
||||||
|
for (MPTerm *term in summands) {
|
||||||
|
value = [value decimalNumberByAdding:[term evaluate]];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFactors:(NSArray *)factors
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
NSDecimalNumber *value = [NSDecimalNumber one];
|
||||||
|
for (MPTerm *term in factors) {
|
||||||
|
value = [value decimalNumberByMultiplyingBy:[term evaluate]];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithVariable:(NSString *)variable
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
return [[MPEvaluationContext sharedContext] valueForVariable:variable];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithSinOfTerm:(MPTerm *)term
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
NSDecimalNumber *sinValue = [term evaluate];
|
||||||
|
return [[NSDecimalNumber alloc] initWithDouble:sin(sinValue.doubleValue)];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithCosOfTerm:(MPTerm *)term
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
NSDecimalNumber *cosValue = [term evaluate];
|
||||||
|
return [[NSDecimalNumber alloc] initWithDouble:cos(cosValue.doubleValue)];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTanOfTerm:(MPTerm *)term
|
||||||
|
{
|
||||||
|
return [self initWithBlock:^NSDecimalNumber *{
|
||||||
|
NSDecimalNumber *tanValue = [term evaluate];
|
||||||
|
return [[NSDecimalNumber alloc] initWithDouble:tan(tanValue.doubleValue)];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)evaluate
|
||||||
|
{
|
||||||
|
return self.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
60
MathPad/MPToken.h
Normal file
60
MathPad/MPToken.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// MPToken.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 19.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, MPTokenType) {
|
||||||
|
MPEOFToken = 0,
|
||||||
|
MPMultiplicationSymbolToken,
|
||||||
|
MPOperatorListToken,
|
||||||
|
MPSinToken,
|
||||||
|
MPCosToken,
|
||||||
|
MPTanToken,
|
||||||
|
MPNumberToken,
|
||||||
|
MPVariableToken,
|
||||||
|
MPEqualsToken,
|
||||||
|
MPGenericFunctionToken,
|
||||||
|
|
||||||
|
MPWhitespaceToken,
|
||||||
|
MPUnidentifiedToken,
|
||||||
|
|
||||||
|
MPCompoundToken
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@protocol MPToken <NSObject>
|
||||||
|
|
||||||
|
- (MPTokenType)tokenType;
|
||||||
|
|
||||||
|
- (NSRange)range;
|
||||||
|
- (NSString *)stringValue;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface MPToken : NSObject <MPToken>
|
||||||
|
|
||||||
|
- (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation;
|
||||||
|
- (instancetype)initWithRange:(NSRange)range inString:(NSString *)input;
|
||||||
|
- (instancetype)initWithTokenType:(MPTokenType)tokenType
|
||||||
|
range:(NSRange)range
|
||||||
|
inString:(NSString *)input;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPToken (MPTokenExtension)
|
||||||
|
// Methods are only available for respective token type
|
||||||
|
|
||||||
|
- (NSUInteger)numberOfOperators;
|
||||||
|
- (NSDecimalNumber *)operatorValue;
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)number;
|
||||||
|
|
||||||
|
- (NSString *)variable;
|
||||||
|
|
||||||
|
@end
|
||||||
104
MathPad/MPToken.m
Normal file
104
MathPad/MPToken.m
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// MPToken.m
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 19.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPToken.h"
|
||||||
|
|
||||||
|
@implementation MPToken {
|
||||||
|
NSRange _range;
|
||||||
|
MPTokenType _tokenType;
|
||||||
|
NSString *_stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_range = NSMakeRange(eofLocation, 0);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithRange:(NSRange)range inString:(NSString *)input
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_range = range;
|
||||||
|
_stringValue = [input substringWithRange:range].copy;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTokenType:(MPTokenType)tokenType
|
||||||
|
range:(NSRange)range
|
||||||
|
inString:(NSString *)input
|
||||||
|
{
|
||||||
|
self = [self initWithRange:range
|
||||||
|
inString:input];
|
||||||
|
if (self) {
|
||||||
|
_tokenType = tokenType;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRange)range
|
||||||
|
{
|
||||||
|
return _range;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPTokenType)tokenType
|
||||||
|
{
|
||||||
|
return _tokenType;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)stringValue
|
||||||
|
{
|
||||||
|
return _stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return self.stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation MPToken (MPTokenExtension)
|
||||||
|
|
||||||
|
- (NSUInteger)numberOfOperators
|
||||||
|
{
|
||||||
|
NSString *operatorString = [[self.stringValue componentsSeparatedByString:@" "] componentsJoinedByString:@""];
|
||||||
|
return operatorString.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)operatorValue
|
||||||
|
{
|
||||||
|
NSString *operatorString = [[self.stringValue componentsSeparatedByString:@" "] componentsJoinedByString:@""];
|
||||||
|
NSDecimalNumber *value = [NSDecimalNumber one];
|
||||||
|
for (NSUInteger charIndex = 0; charIndex < operatorString.length; charIndex++) {
|
||||||
|
NSString *operator = [operatorString substringWithRange:NSMakeRange(charIndex, 1)];
|
||||||
|
if ([operator isEqualToString:@"-"]) {
|
||||||
|
value = [value decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:-1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDecimalNumber *)number
|
||||||
|
{
|
||||||
|
return [NSDecimalNumber decimalNumberWithString:self.stringValue
|
||||||
|
locale:[NSLocale currentLocale]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)variable
|
||||||
|
{
|
||||||
|
return self.stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
27
MathPad/MPTokenStream.h
Normal file
27
MathPad/MPTokenStream.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// MPTokenStream.h
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 20.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MPToken.h"
|
||||||
|
|
||||||
|
@interface MPTokenStream : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithTokens:(NSArray *)tokens;
|
||||||
|
|
||||||
|
@property (nonatomic, copy) NSArray *tokens;
|
||||||
|
@property (nonatomic, getter=isIgnoringWhitespaceTokens) BOOL ignoringWhitespaceTokens; // Default: YES
|
||||||
|
|
||||||
|
@property (readonly, nonatomic) NSUInteger currentLocation;
|
||||||
|
|
||||||
|
- (void)reset;
|
||||||
|
- (BOOL)hasMoreTokens;
|
||||||
|
|
||||||
|
- (MPToken *)nextToken;
|
||||||
|
- (MPToken *)nextTokenOfType:(MPTokenType)type;
|
||||||
|
|
||||||
|
@end
|
||||||
90
MathPad/MPTokenStream.m
Normal file
90
MathPad/MPTokenStream.m
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// MPTokenStream.m
|
||||||
|
// MathPad
|
||||||
|
//
|
||||||
|
// Created by Kim Wittenburg on 20.09.14.
|
||||||
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPTokenStream.h"
|
||||||
|
|
||||||
|
@interface MPTokenStream ()
|
||||||
|
@property (readwrite, nonatomic) NSUInteger currentLocation;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MPTokenStream {
|
||||||
|
NSUInteger currentTokenIndex;
|
||||||
|
NSUInteger eofLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTokens:(NSArray *)tokens
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.tokens = tokens;
|
||||||
|
self.ignoringWhitespaceTokens = YES;
|
||||||
|
if (tokens.count > 0) {
|
||||||
|
eofLocation = NSMaxRange([tokens.lastObject range]);
|
||||||
|
}
|
||||||
|
[self reset];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)skipWhitespaces
|
||||||
|
{
|
||||||
|
if (!self.isIgnoringWhitespaceTokens) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (currentTokenIndex < self.tokens.count) {
|
||||||
|
MPToken *token = self.tokens[currentTokenIndex];
|
||||||
|
if (token.tokenType != MPWhitespaceToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.currentLocation = NSMaxRange(token.range);
|
||||||
|
++currentTokenIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reset
|
||||||
|
{
|
||||||
|
currentTokenIndex = 0;
|
||||||
|
self.currentLocation = 0;
|
||||||
|
[self skipWhitespaces];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasMoreTokens
|
||||||
|
{
|
||||||
|
[self skipWhitespaces];
|
||||||
|
return currentTokenIndex < self.tokens.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPToken *)nextToken
|
||||||
|
{
|
||||||
|
[self skipWhitespaces];
|
||||||
|
if (currentTokenIndex >= self.tokens.count) {
|
||||||
|
return [[MPToken alloc] initEOFTokenAtLocation:eofLocation];
|
||||||
|
} else {
|
||||||
|
MPToken *token = self.tokens[currentTokenIndex++];
|
||||||
|
self.currentLocation = NSMaxRange(token.range);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPToken *)nextTokenOfType:(MPTokenType)type
|
||||||
|
{
|
||||||
|
[self skipWhitespaces];
|
||||||
|
if (currentTokenIndex >= self.tokens.count) {
|
||||||
|
return nil;
|
||||||
|
} else {
|
||||||
|
MPToken *token = self.tokens[currentTokenIndex];
|
||||||
|
if (token.tokenType != type) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
++currentTokenIndex;
|
||||||
|
self.currentLocation = NSMaxRange(token.range);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
Reference in New Issue
Block a user