Invalid Numbers Are Now a Syntax Error
Syntax Errors Now Include a Range Syntax Errors Are Now Displayed in a Selectable List
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
#import "MPFactorialTerm.h"
|
||||
|
||||
#define success() state = 0
|
||||
#define fail() state = -1
|
||||
#define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1
|
||||
|
||||
@interface MPExpressionParser ()
|
||||
|
||||
@@ -85,13 +85,13 @@
|
||||
if (flag && hasVariableDefinition) {
|
||||
result.definedVariable = variableName;
|
||||
} else if (flag) {
|
||||
[self addErrorWithCode:3 localizedDescription:NSLocalizedString(@"Expected Variable Definition.", nil)];
|
||||
[self addErrorWithCode:3 localizedDescription:NSLocalizedString(@"Expected Variable Definition.", nil) useRange:NO];
|
||||
if (errors) {
|
||||
*errors = self.errors;
|
||||
}
|
||||
return nil;
|
||||
} else if (hasVariableDefinition) {
|
||||
[self addErrorWithCode:4 localizedDescription:NSLocalizedString(@"Unexpected Variable Definition.", nil)];
|
||||
[self addErrorWithCode:4 localizedDescription:NSLocalizedString(@"Unexpected Variable Definition.", nil) useRange:NO];
|
||||
if (errors) {
|
||||
*errors = self.errors;
|
||||
}
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
[self skipWhitespaces];
|
||||
if (self.currentTokenIndex >= self.tokens.count) {
|
||||
[self addErrorWithCode:5 localizedDescription:NSLocalizedString(@"Empty Expression.", nil)];
|
||||
[self addErrorWithCode:5 localizedDescription:NSLocalizedString(@"Empty Expression.", nil) useRange:NO];
|
||||
if (errors) {
|
||||
*errors = self.errors;
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
}
|
||||
state = 6;
|
||||
} else {
|
||||
self.value = nil;
|
||||
state = 4;
|
||||
}
|
||||
break;
|
||||
@@ -191,7 +192,6 @@
|
||||
[self.valueGroup addObject:self.value];
|
||||
state = 5;
|
||||
} else {
|
||||
self.errorTokenIndex = self.currentTokenIndex;
|
||||
fail();
|
||||
}
|
||||
break;
|
||||
@@ -376,9 +376,10 @@
|
||||
MPPowerTerm *term = [[MPPowerTerm alloc] initWithFunction:(MPFunction *)self.currentToken errors:&errors];
|
||||
if (!term) {
|
||||
[self.errors addObjectsFromArray:errors];
|
||||
} else {
|
||||
term.baseTerm = self.value;
|
||||
self.value = term;
|
||||
}
|
||||
term.baseTerm = self.value;
|
||||
self.value = term;
|
||||
}
|
||||
[self nextToken];
|
||||
} else {
|
||||
@@ -389,15 +390,22 @@
|
||||
|
||||
- (void)addErrorWithCode:(NSInteger)code
|
||||
localizedDescription:(NSString *)description
|
||||
useRange:(BOOL)flag
|
||||
{
|
||||
NSInteger errorIndex = [self.expression convertIndex:self.errorTokenIndex
|
||||
fromReferenceFrame:MPTokenReferenceFrame
|
||||
toReferenceFrame:MPSymbolReferenceFrame];
|
||||
NSRange errorRange;
|
||||
if (flag) {
|
||||
errorRange = [self.tokens[self.errorTokenIndex] range];
|
||||
} else {
|
||||
NSInteger errorIndex = [self.expression convertIndex:self.errorTokenIndex
|
||||
fromReferenceFrame:MPTokenReferenceFrame
|
||||
toReferenceFrame:MPSymbolReferenceFrame];
|
||||
errorRange = NSMakeRange(errorIndex, 0);
|
||||
}
|
||||
[self.errors addObject:[NSError errorWithDomain:MPMathKitErrorDomain
|
||||
code:code
|
||||
userInfo:@{NSLocalizedDescriptionKey: description,
|
||||
MPPathToExpressionKey: self.expression.indexPath,
|
||||
MPErrorIndexKey: @(errorIndex)}]];
|
||||
MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]];
|
||||
}
|
||||
|
||||
- (BOOL)errorOccured
|
||||
@@ -405,13 +413,15 @@
|
||||
[self skipWhitespaces];
|
||||
if (self.currentTokenIndex < self.tokens.count) {
|
||||
if (self.errorTokenIndex >= self.tokens.count) {
|
||||
[self addErrorWithCode:0 localizedDescription:NSLocalizedString(@"Unexpected end. Expected Value.", nil)];
|
||||
[self addErrorWithCode:0 localizedDescription:NSLocalizedString(@"Unexpected end. Expected Value", nil) useRange:NO];
|
||||
} else {
|
||||
id<MPToken> unexpectedToken = self.tokens[self.errorTokenIndex];
|
||||
if (unexpectedToken.tokenType == MPPowerToken) {
|
||||
[self addErrorWithCode:34 localizedDescription:NSLocalizedString(@"No base for Power", nil)];
|
||||
[self addErrorWithCode:34 localizedDescription:NSLocalizedString(@"No base for Power", nil) useRange:NO];
|
||||
} else if (unexpectedToken.tokenType == MPDeformedNumberToken) {
|
||||
[self addErrorWithCode:42 localizedDescription:NSLocalizedString(@"Not a Number", nil) useRange:YES];
|
||||
} else {
|
||||
[self addErrorWithCode:1 localizedDescription:NSLocalizedString(@"Unexpected Symbol. Expected Value", nil)];
|
||||
[self addErrorWithCode:1 localizedDescription:NSLocalizedString(@"Unexpected Symbol. Expected Value", nil) useRange:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,15 @@
|
||||
NSString *regexStringFormat = @"\\A(?:"
|
||||
@"([\\*∙⋅])|"
|
||||
@"([+-](?:\\s*[+-])*)|"
|
||||
@"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|"
|
||||
@"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@)|" // Substitute with decimal separator 3 times
|
||||
@"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times
|
||||
@"(sin|cos|tan|asin|acos|atan)|"
|
||||
@"([A-Za-z])|"
|
||||
@"(!)|"
|
||||
@"(=)|"
|
||||
@"(\\s+)"
|
||||
@")";
|
||||
NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator];
|
||||
NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator, decimalSeparator, decimalSeparator, decimalSeparator];
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString
|
||||
options:0
|
||||
error:NULL];
|
||||
@@ -69,12 +70,13 @@
|
||||
if (match) {
|
||||
NSRange multiplicationSymbolRange = [match rangeAtIndex:1];
|
||||
NSRange operatorRange = [match rangeAtIndex:2];
|
||||
NSRange numberRange = [match rangeAtIndex:3];
|
||||
NSRange elementaryFunctionRange = [match rangeAtIndex:4];
|
||||
NSRange variableRange = [match rangeAtIndex:5];
|
||||
NSRange factorialRange = [match rangeAtIndex:6];
|
||||
NSRange equalsRange = [match rangeAtIndex:7];
|
||||
NSRange whitespaceRange = [match rangeAtIndex:8];
|
||||
NSRange deformedNumberRange = [match rangeAtIndex:3];
|
||||
NSRange numberRange = [match rangeAtIndex:4];
|
||||
NSRange elementaryFunctionRange = [match rangeAtIndex:5];
|
||||
NSRange variableRange = [match rangeAtIndex:6];
|
||||
NSRange factorialRange = [match rangeAtIndex:7];
|
||||
NSRange equalsRange = [match rangeAtIndex:8];
|
||||
NSRange whitespaceRange = [match rangeAtIndex:9];
|
||||
|
||||
if (MPRangeExists(multiplicationSymbolRange)) {
|
||||
range = multiplicationSymbolRange;
|
||||
@@ -82,6 +84,9 @@
|
||||
} else if (MPRangeExists(operatorRange)) {
|
||||
range = operatorRange;
|
||||
tokenType = MPOperatorListToken;
|
||||
} else if (MPRangeExists(deformedNumberRange)) {
|
||||
range = deformedNumberRange;
|
||||
tokenType = MPDeformedNumberToken;
|
||||
} else if (MPRangeExists(numberRange)) {
|
||||
range = numberRange;
|
||||
tokenType = MPNumberToken;
|
||||
|
||||
@@ -430,23 +430,25 @@
|
||||
return;
|
||||
}
|
||||
self.syntaxErrorsPopUpButton.hidden = NO;
|
||||
[self.syntaxErrorsPopUpButton addItemWithTitle:@""];
|
||||
NSString *title;
|
||||
if (syntaxErrors.count == 1) {
|
||||
NSString *title = NSLocalizedString(@"1 Syntax Error", nil);
|
||||
NSDictionary *attributes = @{NSForegroundColorAttributeName: [NSColor redColor],
|
||||
NSFontAttributeName: [NSFont boldSystemFontOfSize:12.0]};
|
||||
NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:attributes];
|
||||
[self.syntaxErrorsPopUpButton itemAtIndex:0].attributedTitle = attributedTitle;
|
||||
title = NSLocalizedString(@"1 Syntax Error", nil);
|
||||
} else {
|
||||
NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%ld Syntax Errors", nil), syntaxErrors.count];
|
||||
NSDictionary *attributes = @{NSForegroundColorAttributeName: [NSColor redColor],
|
||||
NSFontAttributeName: [NSFont boldSystemFontOfSize:12.0]};
|
||||
NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:attributes];
|
||||
[self.syntaxErrorsPopUpButton itemAtIndex:0].attributedTitle = attributedTitle;
|
||||
title = [NSString stringWithFormat:NSLocalizedString(@"%ld Syntax Errors", nil), syntaxErrors.count];
|
||||
}
|
||||
NSDictionary *attributes = @{NSForegroundColorAttributeName: [NSColor redColor],
|
||||
NSFontAttributeName: [NSFont boldSystemFontOfSize:12.0]};
|
||||
NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:attributes];
|
||||
NSMenuItem *titleItem = [[NSMenuItem alloc] init];
|
||||
titleItem.attributedTitle = attributedTitle;
|
||||
[self.syntaxErrorsPopUpButton.menu addItem:titleItem];
|
||||
|
||||
NSUInteger index = 0;
|
||||
for (NSError *error in syntaxErrors) {
|
||||
[self.syntaxErrorsPopUpButton addItemWithTitle:error.localizedDescription];
|
||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:error.localizedDescription
|
||||
action:NULL
|
||||
keyEquivalent:@""];
|
||||
[self.syntaxErrorsPopUpButton.menu addItem:item];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
@@ -538,8 +540,9 @@
|
||||
{
|
||||
NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1];
|
||||
NSIndexPath *pathToExpression = error.userInfo[MPPathToExpressionKey];
|
||||
pathToExpression = [pathToExpression indexPathByAddingIndex:[error.userInfo[MPErrorIndexKey] integerValue]];
|
||||
self.selection = MPMakeRangePath(pathToExpression, 0);
|
||||
NSRange errorRange = [error.userInfo[MPErrorRangeKey] rangeValue];
|
||||
pathToExpression = [pathToExpression indexPathByAddingIndex:errorRange.location];
|
||||
self.selection = MPMakeRangePath(pathToExpression, errorRange.length);
|
||||
}
|
||||
|
||||
#pragma mark Actions
|
||||
@@ -721,7 +724,7 @@
|
||||
fromReferenceFrame:MPSymbolReferenceFrame
|
||||
toReferenceFrame:MPElementReferenceFrame];
|
||||
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
|
||||
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:1] indexPathByAddingIndex:0], 0);
|
||||
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], 0);
|
||||
}
|
||||
|
||||
- (void)insertNewline:(id)sender
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
|
||||
|
||||
/*!
|
||||
@const MPMathKitErrorDomain
|
||||
@brief Predefined error domain for errors from the MathKit framework.
|
||||
@const MPMathKitErrorDomain
|
||||
@brief Predefined error domain for errors from the MathKit framework.
|
||||
|
||||
@discussion Errors in MathKit can occur during parsing of expressions or
|
||||
during evaluation of expressions. These two can be distinguished
|
||||
@discussion Errors in MathKit can occur during parsing of expressions or
|
||||
during evaluation of expressions. These two can be distinguished
|
||||
by the error code. Parsing errors have lower error codes.
|
||||
Evaluation errors (math errors) have error codes from @c 100 upwards.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPMathKitErrorDomain;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const MPPathToExpressionKey;
|
||||
FOUNDATION_EXPORT NSString *const MPErrorIndexKey;
|
||||
FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
|
||||
|
||||
|
||||
@interface MPParsedExpression : NSObject
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
|
||||
NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey";
|
||||
NSString *const MPErrorIndexKey = @"MPErrorIndexKey";
|
||||
NSString *const MPErrorRangeKey = @"MPErrorRangeKey";
|
||||
|
||||
@implementation MPParsedExpression
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ typedef NS_ENUM(NSUInteger, MPTokenType) {
|
||||
MPOperatorListToken,
|
||||
MPElementaryFunctionToken,
|
||||
MPNumberToken,
|
||||
MPDeformedNumberToken,
|
||||
MPVariableToken,
|
||||
MPEqualsToken,
|
||||
MPFactorialToken,
|
||||
|
||||
Reference in New Issue
Block a user