Archived
1

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:
Kim Wittenburg
2014-11-29 00:19:52 +01:00
parent 4bc2fdead1
commit b6973dc24a
6 changed files with 62 additions and 43 deletions

View File

@@ -25,7 +25,7 @@
#import "MPFactorialTerm.h" #import "MPFactorialTerm.h"
#define success() state = 0 #define success() state = 0
#define fail() state = -1 #define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1
@interface MPExpressionParser () @interface MPExpressionParser ()
@@ -85,13 +85,13 @@
if (flag && hasVariableDefinition) { if (flag && hasVariableDefinition) {
result.definedVariable = variableName; result.definedVariable = variableName;
} else if (flag) { } 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) { if (errors) {
*errors = self.errors; *errors = self.errors;
} }
return nil; return nil;
} else if (hasVariableDefinition) { } 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) { if (errors) {
*errors = self.errors; *errors = self.errors;
} }
@@ -102,7 +102,7 @@
[self skipWhitespaces]; [self skipWhitespaces];
if (self.currentTokenIndex >= self.tokens.count) { 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) { if (errors) {
*errors = self.errors; *errors = self.errors;
} }
@@ -173,6 +173,7 @@
} }
state = 6; state = 6;
} else { } else {
self.value = nil;
state = 4; state = 4;
} }
break; break;
@@ -191,7 +192,6 @@
[self.valueGroup addObject:self.value]; [self.valueGroup addObject:self.value];
state = 5; state = 5;
} else { } else {
self.errorTokenIndex = self.currentTokenIndex;
fail(); fail();
} }
break; break;
@@ -376,9 +376,10 @@
MPPowerTerm *term = [[MPPowerTerm alloc] initWithFunction:(MPFunction *)self.currentToken errors:&errors]; MPPowerTerm *term = [[MPPowerTerm alloc] initWithFunction:(MPFunction *)self.currentToken errors:&errors];
if (!term) { if (!term) {
[self.errors addObjectsFromArray:errors]; [self.errors addObjectsFromArray:errors];
} else {
term.baseTerm = self.value;
self.value = term;
} }
term.baseTerm = self.value;
self.value = term;
} }
[self nextToken]; [self nextToken];
} else { } else {
@@ -389,15 +390,22 @@
- (void)addErrorWithCode:(NSInteger)code - (void)addErrorWithCode:(NSInteger)code
localizedDescription:(NSString *)description localizedDescription:(NSString *)description
useRange:(BOOL)flag
{ {
NSInteger errorIndex = [self.expression convertIndex:self.errorTokenIndex NSRange errorRange;
fromReferenceFrame:MPTokenReferenceFrame if (flag) {
toReferenceFrame:MPSymbolReferenceFrame]; 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 [self.errors addObject:[NSError errorWithDomain:MPMathKitErrorDomain
code:code code:code
userInfo:@{NSLocalizedDescriptionKey: description, userInfo:@{NSLocalizedDescriptionKey: description,
MPPathToExpressionKey: self.expression.indexPath, MPPathToExpressionKey: self.expression.indexPath,
MPErrorIndexKey: @(errorIndex)}]]; MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]];
} }
- (BOOL)errorOccured - (BOOL)errorOccured
@@ -405,13 +413,15 @@
[self skipWhitespaces]; [self skipWhitespaces];
if (self.currentTokenIndex < self.tokens.count) { if (self.currentTokenIndex < self.tokens.count) {
if (self.errorTokenIndex >= 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 { } else {
id<MPToken> unexpectedToken = self.tokens[self.errorTokenIndex]; id<MPToken> unexpectedToken = self.tokens[self.errorTokenIndex];
if (unexpectedToken.tokenType == MPPowerToken) { 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 { } else {
[self addErrorWithCode:1 localizedDescription:NSLocalizedString(@"Unexpected Symbol. Expected Value", nil)]; [self addErrorWithCode:1 localizedDescription:NSLocalizedString(@"Unexpected Symbol. Expected Value", nil) useRange:NO];
} }
} }
} }

View File

@@ -49,14 +49,15 @@
NSString *regexStringFormat = @"\\A(?:" NSString *regexStringFormat = @"\\A(?:"
@"([\\*∙⋅])|" @"([\\*∙⋅])|"
@"([+-](?:\\s*[+-])*)|" @"([+-](?:\\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)|" @"(sin|cos|tan|asin|acos|atan)|"
@"([A-Za-z])|" @"([A-Za-z])|"
@"(!)|" @"(!)|"
@"(=)|" @"(=)|"
@"(\\s+)" @"(\\s+)"
@")"; @")";
NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator]; NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator, decimalSeparator, decimalSeparator, decimalSeparator];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString
options:0 options:0
error:NULL]; error:NULL];
@@ -69,12 +70,13 @@
if (match) { if (match) {
NSRange multiplicationSymbolRange = [match rangeAtIndex:1]; NSRange multiplicationSymbolRange = [match rangeAtIndex:1];
NSRange operatorRange = [match rangeAtIndex:2]; NSRange operatorRange = [match rangeAtIndex:2];
NSRange numberRange = [match rangeAtIndex:3]; NSRange deformedNumberRange = [match rangeAtIndex:3];
NSRange elementaryFunctionRange = [match rangeAtIndex:4]; NSRange numberRange = [match rangeAtIndex:4];
NSRange variableRange = [match rangeAtIndex:5]; NSRange elementaryFunctionRange = [match rangeAtIndex:5];
NSRange factorialRange = [match rangeAtIndex:6]; NSRange variableRange = [match rangeAtIndex:6];
NSRange equalsRange = [match rangeAtIndex:7]; NSRange factorialRange = [match rangeAtIndex:7];
NSRange whitespaceRange = [match rangeAtIndex:8]; NSRange equalsRange = [match rangeAtIndex:8];
NSRange whitespaceRange = [match rangeAtIndex:9];
if (MPRangeExists(multiplicationSymbolRange)) { if (MPRangeExists(multiplicationSymbolRange)) {
range = multiplicationSymbolRange; range = multiplicationSymbolRange;
@@ -82,6 +84,9 @@
} else if (MPRangeExists(operatorRange)) { } else if (MPRangeExists(operatorRange)) {
range = operatorRange; range = operatorRange;
tokenType = MPOperatorListToken; tokenType = MPOperatorListToken;
} else if (MPRangeExists(deformedNumberRange)) {
range = deformedNumberRange;
tokenType = MPDeformedNumberToken;
} else if (MPRangeExists(numberRange)) { } else if (MPRangeExists(numberRange)) {
range = numberRange; range = numberRange;
tokenType = MPNumberToken; tokenType = MPNumberToken;

View File

@@ -430,23 +430,25 @@
return; return;
} }
self.syntaxErrorsPopUpButton.hidden = NO; self.syntaxErrorsPopUpButton.hidden = NO;
[self.syntaxErrorsPopUpButton addItemWithTitle:@""]; NSString *title;
if (syntaxErrors.count == 1) { if (syntaxErrors.count == 1) {
NSString *title = NSLocalizedString(@"1 Syntax Error", nil); 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;
} else { } else {
NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%ld Syntax Errors", nil), syntaxErrors.count]; 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;
} }
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; NSUInteger index = 0;
for (NSError *error in syntaxErrors) { 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++; index++;
} }
} }
@@ -538,8 +540,9 @@
{ {
NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1]; NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1];
NSIndexPath *pathToExpression = error.userInfo[MPPathToExpressionKey]; NSIndexPath *pathToExpression = error.userInfo[MPPathToExpressionKey];
pathToExpression = [pathToExpression indexPathByAddingIndex:[error.userInfo[MPErrorIndexKey] integerValue]]; NSRange errorRange = [error.userInfo[MPErrorRangeKey] rangeValue];
self.selection = MPMakeRangePath(pathToExpression, 0); pathToExpression = [pathToExpression indexPathByAddingIndex:errorRange.location];
self.selection = MPMakeRangePath(pathToExpression, errorRange.length);
} }
#pragma mark Actions #pragma mark Actions
@@ -721,7 +724,7 @@
fromReferenceFrame:MPSymbolReferenceFrame fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame]; toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex]; 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 - (void)insertNewline:(id)sender

View File

@@ -12,18 +12,18 @@
/*! /*!
@const MPMathKitErrorDomain @const MPMathKitErrorDomain
@brief Predefined error domain for errors from the MathKit framework. @brief Predefined error domain for errors from the MathKit framework.
@discussion Errors in MathKit can occur during parsing of expressions or @discussion Errors in MathKit can occur during parsing of expressions or
during evaluation of expressions. These two can be distinguished during evaluation of expressions. These two can be distinguished
by the error code. Parsing errors have lower error codes. by the error code. Parsing errors have lower error codes.
Evaluation errors (math errors) have error codes from @c 100 upwards. Evaluation errors (math errors) have error codes from @c 100 upwards.
*/ */
FOUNDATION_EXPORT NSString *const MPMathKitErrorDomain; FOUNDATION_EXPORT NSString *const MPMathKitErrorDomain;
FOUNDATION_EXPORT NSString *const MPPathToExpressionKey; FOUNDATION_EXPORT NSString *const MPPathToExpressionKey;
FOUNDATION_EXPORT NSString *const MPErrorIndexKey; FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
@interface MPParsedExpression : NSObject @interface MPParsedExpression : NSObject

View File

@@ -14,7 +14,7 @@
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain"; NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey"; NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey";
NSString *const MPErrorIndexKey = @"MPErrorIndexKey"; NSString *const MPErrorRangeKey = @"MPErrorRangeKey";
@implementation MPParsedExpression @implementation MPParsedExpression

View File

@@ -65,6 +65,7 @@ typedef NS_ENUM(NSUInteger, MPTokenType) {
MPOperatorListToken, MPOperatorListToken,
MPElementaryFunctionToken, MPElementaryFunctionToken,
MPNumberToken, MPNumberToken,
MPDeformedNumberToken,
MPVariableToken, MPVariableToken,
MPEqualsToken, MPEqualsToken,
MPFactorialToken, MPFactorialToken,