// // MPExpressionLexer.m // MathPad // // Created by Kim Wittenburg on 19.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPExpressionTokenizer.h" #import "MPExpression.h" #import "MPExpressionElement.h" #import "MPToken.h" #import "NSRegularExpression+MPParsingAdditions.h" #define MPRangeExists(range) (range.location != NSNotFound) @implementation MPExpressionTokenizer + (NSArray *)tokenizeExpression:(MPExpression *)expression { NSMutableArray *tokens = [[NSMutableArray alloc] init]; NSUInteger symbolIndex = 0; for (NSUInteger index = 0; index < [expression countItemsInReferenceFrame:MPElementReferenceFrame]; index++) { id element = [expression itemAtIndex:index referenceFrame:MPElementReferenceFrame]; if ([element isFunction]) { [tokens addObject:element]; } else { [tokens addObjectsFromArray:[self tokenizeElement:(NSString *)element elementSymbolIndex:symbolIndex]]; } symbolIndex += element.length; } return tokens; } + (NSArray *)tokenizeElement:(NSString *)element elementSymbolIndex:(NSUInteger)symbolIndex { NSUInteger lexLocation = 0; NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]]; NSString *regexStringFormat = @"\\A(?:" @"([\\*∙⋅])|" @"([+-](?:\\s*[+-])*)|" @"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" @"(sin)|" @"(cos)|" @"(tan)|" @"(asin)|" @"(acos)|" @"(atan)|" @"([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 asinRange = [match rangeAtIndex:7]; NSRange acosRange = [match rangeAtIndex:8]; NSRange atanRange = [match rangeAtIndex:9]; NSRange variableRange = [match rangeAtIndex:10]; NSRange factorialRange = [match rangeAtIndex:11]; NSRange equalsRange = [match rangeAtIndex:12]; NSRange whitespaceRange = [match rangeAtIndex:13]; 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(asinRange)) { range = asinRange; tokenType = MPASinToken; } else if (MPRangeExists(acosRange)) { range = acosRange; tokenType = MPACosToken; } else if (MPRangeExists(atanRange)) { range = atanRange; tokenType = MPATanToken; } else if (MPRangeExists(variableRange)) { range = variableRange; tokenType = MPVariableToken; } else if (MPRangeExists(factorialRange)) { range = factorialRange; tokenType = MPFactorialToken; } 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); NSString *tokenStringValue = [element substringWithRange:range]; range.location += symbolIndex; [tokens addObject:[[MPToken alloc] initWithTokenType:tokenType range:range stringValue:tokenStringValue]]; } return tokens; } @end