Archived
1
This repository has been archived on 2022-08-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mathpad/MathPad/MPExpressionParser.m
2014-11-24 22:42:44 +01:00

417 lines
13 KiB
Objective-C

//
// MPExpressionParser.m
// MathPad
//
// Created by Kim Wittenburg on 16.11.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionParser.h"
#import "MPExpression.h"
#import "MPTokenStream.h"
#import "MPTerm.h"
#import "MPToken.h"
#import "MPSumTerm.h"
#import "MPProductTerm.h"
#import "MPFactorialTerm.h"
#import "MPElementaryFunctionTerm.h"
#import "MPFunctionTerm.h"
#import "MPPowerTerm.h"
#import "MPNegatedTerm.h"
#import "MPNumber.h"
#import "MPVariable.h"
#import "MPFactorialTerm.h"
#define success() state = 0
#define fail() state = -1
@interface MPExpressionParser ()
@property (nonatomic, strong) NSArray *tokens;
@property (nonatomic) NSUInteger currentTokenIndex;
@property (nonatomic, strong) NSMutableArray *errors;
@property (nonatomic, strong) NSMutableArray *summands;
@property (nonatomic) BOOL summandNegative;
@property (nonatomic, strong) NSMutableArray *factors;
@property (nonatomic) BOOL factorNegative;
@property (nonatomic, strong) NSMutableArray *functionStack;
@property (nonatomic, strong) NSMutableArray *valueGroup;
@property (nonatomic, strong) MPTerm *value;
@property (nonatomic) NSUInteger errorTokenIndex;
@end
@implementation MPExpressionParser
- (instancetype)initWithExpression:(MPExpression *)expression
{
self = [super init];
if (self) {
_tokens = [expression allItemsInReferenceFrame:MPTokenReferenceFrame];
_expression = expression;
}
return self;
}
- (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors
{
return [self parseExpectingVariableDefinition:NO
errors:errors];
}
- (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag
errors:(NSArray *__autoreleasing *)errors
{
MPParsedExpression *result = [[MPParsedExpression alloc] init];
self.currentTokenIndex = 0;
BOOL hasVariableDefinition = NO;
NSString *variableName = nil;
[self skipWhitespaces];
if (self.currentToken.tokenType == MPVariableToken) {
variableName = self.currentToken.stringValue;
[self nextToken];
[self skipWhitespaces];
if (self.currentToken.tokenType == MPEqualsToken) {
[self nextToken];
hasVariableDefinition = YES;
}
}
if (flag && hasVariableDefinition) {
result.definedVariable = variableName;
} else if (flag) {
[self addErrorWithCode:3 localizedDescription:NSLocalizedString(@"Expected Variable Definition.", nil)];
if (errors) {
*errors = self.errors;
}
return nil;
} else if (hasVariableDefinition) {
[self addErrorWithCode:4 localizedDescription:NSLocalizedString(@"Unexpected Variable Definition.", nil)];
if (errors) {
*errors = self.errors;
}
return nil;
} else {
self.currentTokenIndex = 0;
}
[self skipWhitespaces];
if (self.currentTokenIndex >= self.tokens.count) {
[self addErrorWithCode:5 localizedDescription:NSLocalizedString(@"Empty Expression.", nil)];
if (errors) {
*errors = self.errors;
}
return nil;
}
[self runParserMachine];
if ([self errorOccured]) {
if (errors) {
*errors = self.errors;
}
return nil;
}
result.term = [[MPSumTerm alloc] initWithSummands:self.summands];
return result;
}
- (void)runParserMachine
{
NSInteger state = 1;
NSInteger backtraceState = 0;
NSUInteger backtraceTokenIndex = self.currentTokenIndex;
BOOL alternateRoute = NO;
while (state != 0) {
switch (state) {
case -1:
state = backtraceState;
self.currentTokenIndex = backtraceTokenIndex;
alternateRoute = YES;
break;
case 1:
[self skipWhitespaces];
if (self.currentToken.tokenType == MPOperatorListToken) {
self.factorNegative = [self parseOperatorList:self.currentToken];
[self nextToken];
state = 2;
} else {
self.factorNegative = NO;
state = 2;
}
break;
case 2:
[self skipWhitespaces];
if (self.currentToken.tokenType == MPElementaryFunctionToken) {
[self.functionStack addObject:self.currentToken.stringValue];
[self nextToken];
state = 2;
} else {
state = 3;
}
break;
case 3:
[self skipWhitespaces];
if (self.currentToken.tokenType == MPGenericFunctionToken) {
NSArray *errors;
self.value = [[MPFunctionTerm alloc] initWithFunction:(MPFunction *)self.currentToken
errors:&errors];
if (!self.value) {
[self.errors addObjectsFromArray:errors];
}
[self nextToken];
[self runSuffixMachine];
if (self.value) {
[self.valueGroup addObject:self.value];
}
state = 6;
} else {
state = 4;
}
break;
case 4:
if (self.currentToken.tokenType == MPNumberToken) {
self.value = [[MPNumber alloc] initWithNumber:[self parseNumber:self.currentToken]];
[self nextToken];
[self runSuffixMachine];
[self.valueGroup addObject:self.value];
state = 5;
} else if (self.currentToken.tokenType == MPVariableToken) {
self.value = [[MPVariable alloc] initWithVariableName:[self parseVariable:self.currentToken]];
[self nextToken];
[self runSuffixMachine];
[self.valueGroup addObject:self.value];
state = 5;
} else {
self.errorTokenIndex = self.currentTokenIndex;
fail();
}
break;
case 5:
if (alternateRoute) {
alternateRoute = NO;
state = 6;
} else {
backtraceState = 5;
backtraceTokenIndex = self.currentTokenIndex;
state = 4;
}
break;
case 6:
[self collapseFactor];
[self skipWhitespaces];
backtraceState = 6;
backtraceTokenIndex = self.currentTokenIndex;
if (!alternateRoute && self.currentToken.tokenType == MPOperatorListToken) {
[self collapseSummand];
self.summandNegative = [self parseOperatorList:self.currentToken];
[self nextToken];
state = 1;
} else {
state = 7;
}
break;
case 7:
if (alternateRoute) {
[self collapseSummand];
alternateRoute = NO;
success();
} else {
backtraceState = 7;
backtraceTokenIndex = self.currentTokenIndex;
state = 8;
}
break;
case 8:
[self skipWhitespaces];
if (self.currentToken.tokenType == MPMultiplicationSymbolToken) {
[self nextToken];
state = 1;
} else {
state = 1;
}
break;
default:
// Should never get here
break;
}
}
}
- (void)skipWhitespaces
{
while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) {
[self nextToken];
}
}
- (id<MPToken>)currentToken
{
if (self.currentTokenIndex >= self.tokens.count) {
return nil;
}
return self.tokens[self.currentTokenIndex];
}
- (void)nextToken
{
self.currentTokenIndex++;
}
- (NSMutableArray *)errors
{
if (!_errors) {
_errors = [[NSMutableArray alloc] init];
}
return _errors;
}
- (NSMutableArray *)summands
{
if (!_summands) {
_summands = [[NSMutableArray alloc] init];
}
return _summands;
}
- (NSMutableArray *)factors
{
if (!_factors) {
_factors = [[NSMutableArray alloc] init];
}
return _factors;
}
- (NSMutableArray *)functionStack
{
if (!_functionStack) {
_functionStack = [[NSMutableArray alloc] init];
}
return _functionStack;
}
- (NSMutableArray *)valueGroup
{
if (!_valueGroup) {
_valueGroup = [[NSMutableArray alloc] init];
}
return _valueGroup;
}
- (BOOL)parseOperatorList:(id<MPToken>)token // Returns YES if list is overall negative
{
NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""]
stringByReplacingOccurrencesOfString:@"+" withString:@""];
return (operatorString.length & 1) == 1;
}
- (NSDecimalNumber *)parseNumber:(id<MPToken>)token
{
return [NSDecimalNumber decimalNumberWithString:token.stringValue
locale:[NSLocale currentLocale]];
}
- (NSString *)parseVariable:(id<MPToken>)token
{
return token.stringValue;
}
- (void)collapseSummand
{
if (self.factors.count > 0) {
MPTerm *summand = [[MPProductTerm alloc] initWithFactors:self.factors];
if (self.summandNegative) {
summand = [[MPNegatedTerm alloc] initWithTerm:summand];
}
[self.summands addObject:summand];
}
self.factors = nil;
self.summandNegative = NO;
}
- (void)collapseFactor
{
if (self.valueGroup.count > 0) {
MPTerm *factor = [[MPProductTerm alloc] initWithFactors:self.valueGroup];
for (NSInteger index = self.functionStack.count - 1; index >= 0; index--) {
factor = [[MPElementaryFunctionTerm alloc] initWithFunctionIdentifier:self.functionStack[index]
term:factor];
}
if (self.factorNegative) {
factor = [[MPNegatedTerm alloc] initWithTerm:factor];
}
[self.factors addObject:factor];
}
self.valueGroup = nil;
self.functionStack = nil;
self.factorNegative = NO;
}
- (void)runSuffixMachine
{
BOOL checkMore = YES;
while (checkMore) {
if (self.currentToken.tokenType == MPFactorialToken) {
[self nextToken];
if (self.value) {
MPFactorialTerm *term = [[MPFactorialTerm alloc] initWithTerm:self.value];
self.value = term;
}
} else if (self.currentToken.tokenType == MPPowerToken) {
[self nextToken];
if (self.value) {
NSArray *errors;
MPPowerTerm *term = [[MPPowerTerm alloc] initWithFunction:(MPFunction *)self.currentToken errors:&errors];
if (!term) {
[self.errors addObjectsFromArray:errors];
}
term.baseTerm = self.value;
self.value = term;
}
} else {
checkMore = NO;
}
}
}
- (void)addErrorWithCode:(NSInteger)code
localizedDescription:(NSString *)description
{
NSInteger errorIndex = [self.expression convertIndex:self.errorTokenIndex
fromReferenceFrame:MPTokenReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
[self.errors addObject:[NSError errorWithDomain:MPMathKitErrorDomain
code:code
userInfo:@{NSLocalizedDescriptionKey: description,
MPPathToExpressionKey: self.expression.indexPath,
MPErrorIndexKey: @(errorIndex)}]];
}
- (BOOL)errorOccured
{
[self skipWhitespaces];
if (self.currentTokenIndex < self.tokens.count) {
if (self.errorTokenIndex >= self.tokens.count) {
[self addErrorWithCode:0 localizedDescription:NSLocalizedString(@"Unexpected end. Expected Value.", nil)];
} else {
[self addErrorWithCode:1 localizedDescription:NSLocalizedString(@"Unexpected Symbol. Expected Value", nil)];
}
}
return self.errors.count > 0;
}
@end