245 lines
7.3 KiB
Objective-C
245 lines
7.3 KiB
Objective-C
//
|
|
// MPExpressionEvaluator.m
|
|
// MathPad
|
|
//
|
|
// Created by Kim Wittenburg on 31.08.14.
|
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
|
//
|
|
|
|
#import "MPExpressionEvaluator.h"
|
|
#import "MPExpression.h"
|
|
#import "MPFunction.h"
|
|
#import "MPMathRules.h"
|
|
#import "MPToken.h"
|
|
#import "MPFunction+MPToken.h"
|
|
#import "MPTokenStream.h"
|
|
|
|
#import "MPEvaluationContext.h"
|
|
|
|
|
|
|
|
@interface MPExpressionEvaluator ()
|
|
@property (readwrite, nonatomic, copy) NSString *definedVariable;
|
|
@property (nonatomic, strong) NSArray *tokens;
|
|
|
|
- (void)setError:(MPParseError *)error;
|
|
|
|
@end
|
|
|
|
@implementation MPExpressionEvaluator {
|
|
MPParseError *__autoreleasing *_error;
|
|
MPTokenStream *tokenStream;
|
|
}
|
|
|
|
- (id)initWithExpression:(MPExpression *)expression
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_expression = expression;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
@synthesize lexer = _lexer;
|
|
|
|
- (void)setLexer:(MPExpressionTokenizer *)lexer
|
|
{
|
|
_lexer = lexer;
|
|
self.tokens = nil;
|
|
}
|
|
|
|
- (MPExpressionTokenizer *)lexer
|
|
{
|
|
if (!_lexer) {
|
|
_lexer = [[MPExpressionTokenizer alloc] init];
|
|
}
|
|
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;
|
|
}
|
|
|
|
// Variable Definition
|
|
if (flag) {
|
|
MPToken *variableToken = [tokenStream nextToken];
|
|
MPToken *equalsToken = [tokenStream nextTokenOfType:MPEqualsToken];
|
|
|
|
if (variableToken.tokenType != MPVariableToken) {
|
|
self.error = MPParseError(variableToken.range, @"Expected Variable Definition.");
|
|
return nil;
|
|
}
|
|
if (equalsToken.tokenType != MPEqualsToken) {
|
|
self.error = MPParseError(equalsToken.range, @"Expected \"=\".");
|
|
return nil;
|
|
}
|
|
self.definedVariable = variableToken.stringValue;
|
|
}
|
|
|
|
BOOL isAtBeginning = YES;
|
|
NSMutableArray *summands = [[NSMutableArray alloc] init];
|
|
NSMutableArray *currentSummand = [[NSMutableArray alloc] init];
|
|
|
|
while (tokenStream.hasMoreTokens) {
|
|
MPToken *multiplicationToken = [tokenStream nextTokenOfType:MPMultiplicationSymbolToken];
|
|
MPToken *operatorToken = [tokenStream nextTokenOfType:MPOperatorListToken];
|
|
NSRange valueRange;
|
|
MPTerm *value = [self nextValue:&valueRange];
|
|
|
|
if (multiplicationToken) {
|
|
if (isAtBeginning) {
|
|
self.error = MPParseError(multiplicationToken.range, @"Unexpected Symbol. Expected Number.");
|
|
return nil;
|
|
}
|
|
if (operatorToken) {
|
|
if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) {
|
|
self.error = MPParseError(operatorToken.range, @"Too many operators in multiplication");
|
|
return nil;
|
|
}
|
|
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
|
|
}
|
|
if (value) {
|
|
[currentSummand addObject:value];
|
|
} else {
|
|
return nil;
|
|
}
|
|
} else {
|
|
if (operatorToken) {
|
|
if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLength]) {
|
|
self.error = MPParseError(operatorToken.range, @"Too many operators.");
|
|
return nil;
|
|
}
|
|
[summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]];
|
|
currentSummand = [[NSMutableArray alloc] init];
|
|
[currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]];
|
|
if (value) {
|
|
[currentSummand addObject:value];
|
|
} else {
|
|
return nil;
|
|
}
|
|
} 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|