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
2014-09-13 23:16:44 +02:00

318 lines
12 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"
#import "MPParsedFactor.h"
#import "MPParsedNumber.h"
#import "MPParsedVariable.h"
#import "MPParsedOperator.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;
- (MPParsedOperator *)parseOperators;
- (MPParsedNumber *)parseNumber;
@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:(id<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:(id<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 (YES) {
[self parseWhitespaces];
NSRange multiplicationSymbolRange = [self parseMultiplicationSymbol];
BOOL hasMultiplicationSymbol = multiplicationSymbolRange.location != NSNotFound;
MPParsedOperator *operators = [self parseOperators];
// NSRange functionRange = ...
// BOOL hasFunction = functionRange.location != NSNotFound;
MPParsedNumber *number = [self parseNumber];
if (!number.exists) {
if ([self isAtEnd] && nextFactor != nil) {
if (hasMultiplicationSymbol) {
if (operators.numberOfOperators > self.maximumOperatorChainLengthInFunction) {
self.error = MPParseError(operators.range, @"Too many operators in multiplication.");
return nil;
}
[currentProduct addFactor:operators];
} else if (operators.exists) {
if (operators.numberOfOperators > self.maximumOperatorChainLength) {
self.error = MPParseError(operators.range, @"Too many operators.");
return nil;
}
[products addObject:currentProduct];
currentProduct = [[MPParsedProduct alloc] init];
[currentProduct addFactor:operators];
} else if (self.allowsImplicitMultiplications) {
NSLog(@"Here I am");
if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] init];
}
} else {
NSLog(@"ERR");
self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Implicit Multiplication not allowed.");
return nil;
}
break;
} else if ([self isAtEnd]) {
if (hasMultiplicationSymbol || operators.exists) {
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;
}
break;
} else {
if (hasMultiplicationSymbol) {
if (currentProduct) {
if (operators.numberOfOperators > self.maximumOperatorChainLengthInMultiplication) {
self.error = MPParseError(operators.range, @"Too many operators in multiplication.");
return nil;
}
[currentProduct addFactor:operators];
[currentProduct addFactor:number];
} else {
self.error = MPParseError(multiplicationSymbolRange, @"Unexpected Symbol. Expected Number.");
return nil;
}
} else if (operators.exists) {
if (operators.numberOfOperators > self.maximumOperatorChainLength) {
self.error = MPParseError(operators.range, @"Too many operators.");
return nil;
}
if (currentProduct) {
[products addObject:currentProduct];
}
currentProduct = [[MPParsedProduct alloc] initWithFactor:operators];
[currentProduct addFactor:number];
} else if (!currentProduct) {
currentProduct = [[MPParsedProduct alloc] initWithFactor:number];
} else if (self.allowsImplicitMultiplications) {
[currentProduct addFactor:number];
} else {
self.error = MPParseError(NSMakeRange(number.range.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;
- (MPParsedOperator *)parseOperators
{
if (!operatorsRegex) {
operatorsRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([+-](?:\\s*[+-])*)\\s*"
options:0
error:NULL];
}
NSTextCheckingResult *match = [operatorsRegex firstMatchInString:self.input
fromIndex:self.parsePosition];
NSRange matchRange;
if (!match) {
matchRange = NSMakeRange(NSNotFound, 0);
} else {
self.parsePosition = NSMaxRange(match.range);
matchRange = [match rangeAtIndex:1];
}
return [[MPParsedOperator alloc] initWithRange:matchRange
inString:self.input];
}
- (MPParsedNumber *)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];
NSRange matchRange;
if (!match) {
matchRange = NSMakeRange(NSNotFound, 0);
} else {
self.parsePosition = NSMaxRange(match.range);
matchRange = [match rangeAtIndex:1];
}
return [[MPParsedNumber alloc] initWithRange:matchRange
inString:self.input];
}
@end