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/MPElementParser.m

337 lines
13 KiB
Objective-C

//
// MPElementParser.m
// MathPad
//
// Created by Kim Wittenburg on 10.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPElementParser.h"
#import "NSRegularExpression+MPParsingAdditions.h"
#define MPMultiplicationSymbol @"*"
@interface MPElementParser ()
@property (nonatomic) NSString *input;
@property (nonatomic) NSUInteger parsePosition;
- (void)setError:(MPParseError *)error;
- (BOOL)isAtEnd;
- (NSRange)parseVariableDefinition:(NSString *__autoreleasing *)variableName;
- (NSRange)parseWhitespaces;
- (NSRange)parseMultiplicationSymbol;
- (NSRange)parseOperators;
- (NSUInteger)countOperatorsInRange:(NSRange)range
multiplicator:(out NSDecimalNumber *__autoreleasing *)multiplicator;
- (NSRange)parseNumber;
- (NSDecimalNumber *)numberInRange:(NSRange)range;
@end
@implementation MPElementParser {
MPParseError *__autoreleasing *outError;
}
static BOOL useUserDefaults;
#pragma mark Creation Methods
+ (void)initialize
{
useUserDefaults = YES;
}
- (instancetype)init
{
self = [super init];
if (self) {
self.allowsImplicitMultiplications = NO;
self.maximumOperatorChainLength = 2;
self.maximumOperatorChainLengthInMultiplication = 1;
}
return self;
}
#pragma mark Properties
+ (BOOL)isUsingDefaultValuesFromUserDefaults
{
return useUserDefaults;
}
+ (void)setUsingDefaultValuesFromUserDefaults:(BOOL)flag
{
useUserDefaults = flag;
}
#pragma mark Parsing Methods
- (BOOL)isAtEnd
{
return self.parsePosition >= self.input.length;
}
- (void)setError:(MPParseError *)error
{
if (outError != NULL) {
*outError = error;
}
}
- (NSArray *)parseElement:(NSString *)string
previousProduct:(MPParsedProduct *)previousProduct
nextFactor:(MPParsedFactor *)nextFactor
error:(out MPParseError *__autoreleasing *)error
{
return [self parseElement:string
previousProduct:previousProduct
nextFactor:nextFactor
definesVariable:NO
definedVariable:NULL
error:error];
}
- (NSArray *)parseElement:(NSString *)string
previousProduct:(MPParsedProduct *)previousProduct
nextFactor:(MPParsedFactor *)nextFactor
definesVariable:(BOOL)flag
definedVariable:(NSString *__autoreleasing *)variableName
error:(out MPParseError *__autoreleasing *)error
{
self.input = string;
outError = error;
self.parsePosition = 0;
NSString *definedVariable;
NSRange variableDefinitionRange = [self parseVariableDefinition:&definedVariable];
if (flag) {
if (!definedVariable) {
self.error = MPParseError(NSMakeRange(0, 0), @"Expected Variable Definition");
return nil;
} else if (variableName) {
*variableName = definedVariable;
}
} else if (definedVariable) {
self.error = MPParseError(variableDefinitionRange, @"Unexpected Variable Definition");
return nil;
}
NSMutableArray *products = [[NSMutableArray alloc] init];
MPParsedProduct *currentProduct = previousProduct;
while (![self isAtEnd]) {
[self parseWhitespaces];
NSRange multiplicationSymbolRange = [self parseMultiplicationSymbol];
BOOL hasMultiplicationSymbol = multiplicationSymbolRange.location != NSNotFound;
NSRange operatorsRange = [self parseOperators];
BOOL hasOperators = operatorsRange.location != NSNotFound;
// NSRange functionRange = ...
// BOOL hasFunction = functionRange.location != NSNotFound;
NSRange numberRange = [self parseNumber];
BOOL hasNumber = numberRange.location != NSNotFound;
NSDecimalNumber *operatorMultiplicator;
NSUInteger operatorCount = [self countOperatorsInRange:operatorsRange
multiplicator:&operatorMultiplicator];
if (!hasNumber) {
if ([self isAtEnd] && nextFactor != nil) {
if (hasMultiplicationSymbol) {
if (operatorCount > self.maximumOperatorChainLengthInFunction) {
self.error = MPParseError(operatorsRange, @"Too many operators in multiplication.");
return nil;
}
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:operatorMultiplicator]];
} else if (hasOperators) {
if (operatorCount > self.maximumOperatorChainLength) {
self.error = MPParseError(operatorsRange, @"Too many operators.");
return nil;
}
[products addObject:currentProduct];
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:[[NSDecimalNumber alloc] initWithUnsignedInteger:operatorCount]]];
} else if (self.allowsImplicitMultiplications) {
if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] init];
}
} else {
self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Implicit Multiplication not allowed.");
return nil;
}
break;
} else if ([self isAtEnd]) {
self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Unexpected End. Expected Number.");
return nil;
} else {
self.error = MPParseError(NSMakeRange(self.parsePosition, 1), @"Unexpected Symbol. Expected Number.");
return nil;
}
} else {
NSDecimalNumber *number = [self numberInRange:numberRange];
NSDecimalNumber *value = [operatorMultiplicator decimalNumberByMultiplyingBy:number];
if (hasMultiplicationSymbol) {
if (currentProduct) {
if (operatorCount > self.maximumOperatorChainLengthInMultiplication) {
self.error = MPParseError(operatorsRange, @"Too many operators in multiplication.");
return nil;
}
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else {
self.error = MPParseError(multiplicationSymbolRange, @"Unexpected Symbol. Expected Number.");
return nil;
}
} else if (hasOperators) {
if (operatorCount > self.maximumOperatorChainLength) {
self.error = MPParseError(operatorsRange, @"Too many operators.");
return nil;
}
if (currentProduct) {
[products addObject:currentProduct];
}
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else if (self.allowsImplicitMultiplications) {
[currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]];
} else {
self.error = MPParseError(NSMakeRange(numberRange.location, 0), @"Implicit Multiplication not allowed.");
return nil;
}
}
}
if (nextFactor) {
[currentProduct addFactor:nextFactor];
}
[products addObject:currentProduct];
return products;
}
static NSRegularExpression *variableDefinitionRegex;
- (NSRange)parseVariableDefinition:(NSString *__autoreleasing *)variableName
{
if (!variableDefinitionRegex) {
variableDefinitionRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([A-Za-z])\\s*=\\s*"
options:0
error:NULL];
}
NSTextCheckingResult *match = [variableDefinitionRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
if (!match) {
return NSMakeRange(NSNotFound, 0);
}
self.parsePosition = NSMaxRange(match.range);
NSRange variableDefinitionRange = [match rangeAtIndex:1];
*variableName = [self.input substringWithRange:variableDefinitionRange];
return variableDefinitionRange;
}
static NSRegularExpression *whitespaceRegex;
- (NSRange)parseWhitespaces
{
if (!whitespaceRegex) {
whitespaceRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*"
options:0
error:NULL];
}
NSTextCheckingResult *match = [whitespaceRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
if (match) {
self.parsePosition = NSMaxRange(match.range);
return match.range;
} else {
return NSMakeRange(NSNotFound, 0);
}
}
static NSRegularExpression *multiplicationSymbolRegex;
- (NSRange)parseMultiplicationSymbol
{
if (!multiplicationSymbolRegex) {
NSString *multiplicationSymbolString = [NSRegularExpression escapedPatternForString:MPMultiplicationSymbol];
NSString *multiplicationSymbolRegexString = [NSString stringWithFormat:@"\\A\\s*(%@)\\s*", multiplicationSymbolString];
multiplicationSymbolRegex = [NSRegularExpression regularExpressionWithPattern:multiplicationSymbolRegexString
options:0
error:NULL];
}
NSTextCheckingResult *match = [multiplicationSymbolRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
if (!match) {
return NSMakeRange(NSNotFound, 0);
}
self.parsePosition = NSMaxRange(match.range);
return [match rangeAtIndex:1];
}
static NSRegularExpression *operatorsRegex;
- (NSRange)parseOperators
{
if (!operatorsRegex) {
operatorsRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([+-](?:\\s*[+-])*)\\s*"
options:0
error:NULL];
}
NSTextCheckingResult *match = [operatorsRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
if (match == nil) {
return NSMakeRange(NSNotFound, 0);
}
self.parsePosition = NSMaxRange(match.range);
return [match rangeAtIndex:1];
}
- (NSUInteger)countOperatorsInRange:(NSRange)range
multiplicator:(out NSDecimalNumber *__autoreleasing *)outMultiplicator
{
if (range.location == NSNotFound) {
*outMultiplicator = [NSDecimalNumber one];
return 0;
}
NSString *operatorsString = [self.input substringWithRange:range];
NSString *operators = [[operatorsString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
NSInteger multiplicator = 1;
for (NSUInteger characterIndex; characterIndex < operators.length; characterIndex++) {
if ([[operators substringWithRange:NSMakeRange(characterIndex, 1)] isEqualToString:@"-"]) {
multiplicator *= -1;
}
}
*outMultiplicator = [[NSDecimalNumber alloc] initWithInteger:multiplicator];
return operators.length;
}
- (NSRange)parseNumber
{
NSString *decimalSeparatorRegexString = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]];
NSString *numberRegexFormat = [NSString stringWithFormat:@"\\A\\s*((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))\\s*", decimalSeparatorRegexString, decimalSeparatorRegexString];
NSRegularExpression *numberRegex = [NSRegularExpression regularExpressionWithPattern:numberRegexFormat
options:0
error:NULL];
NSTextCheckingResult *match = [numberRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
if (!match) {
return NSMakeRange(NSNotFound, 0);
}
self.parsePosition = NSMaxRange(match.range);
return [match rangeAtIndex:1];
}
- (NSDecimalNumber *)numberInRange:(NSRange)range
{
NSString *numberString = [self.input substringWithRange:range];
return [NSDecimalNumber decimalNumberWithString:numberString];
}
@end