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"
#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];
}
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

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

View File

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