diff --git a/SA_DiceParser.m b/SA_DiceParser.m index 9cafff7..e46c439 100644 --- a/SA_DiceParser.m +++ b/SA_DiceParser.m @@ -180,7 +180,70 @@ static NSDictionary *_validCharactersDict; { NSString *operator = [dieRollString substringWithRange:lastOperatorRange]; - return [self legacyExpressionForStringDescribingOperation:dieRollString withOperator:operator atRange:lastOperatorRange]; + // If the last (and thus only) operator is the leading character of + // the expression, then this is one of several possible special cases. + if(lastOperatorRange.location == 0) + { + NSMutableDictionary *expression; + + // First, we check for whether there even is anything more to the + // roll string besides the operator. If not, then the string is + // definitely malformed... + if(dieRollString.length == lastOperatorRange.length) + { + expression = [NSMutableDictionary dictionary]; + expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; + expression[SA_DB_INPUT_STRING] = dieRollString; + + addErrorToExpression(SA_DB_ERROR_INVALID_EXPRESSION, expression); + + return expression; + } + + // If the last operator is the leading character (i.e. there's just + // one operator in the expression, and it's at the beginning), and + // there's more to the expression than just the operator, then + // this is either an expression whose first term (which may or may + // not be its only term) is a simple value expression which + // represents a negative number - or, it's a malformed expression + // (because operators other than negation cannot begin an + // expression). + // In the former case, we do nothing, letting the testing for + // expression type fall through to the remaining cases (roll command + // or simple value). + // In the latter case, we register an error and return. + if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_MINUS] containsCharactersInString:operator]) + { + // We've determined that this expression begins with a simple + // value expression that represents a negative number. + // This next line is a hack to account for the fact that Cocoa's + // Unicode compliance is incomplete. :( NSString's integerValue + // method only accepts the hyphen as a negation sign when reading a + // number - not any of the Unicode characters which officially + // symbolize negation! But we are more modern-minded, and accept + // arbitrary symbols as minus-sign. For proper parsing, though, + // we have to replace it like this... + dieRollString = [dieRollString stringByReplacingCharactersInRange:lastOperatorRange withString:@"-"]; + + // Now we skip the remainder of the "is it an operator?" code + // and fall through to "is it a roll command, or maybe a simple + // value?"... + } + else + { + expression = [NSMutableDictionary dictionary]; + expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; + expression[SA_DB_INPUT_STRING] = dieRollString; + + addErrorToExpression(SA_DB_ERROR_INVALID_EXPRESSION, expression); + + return expression; + } + } + else + { + return [self legacyExpressionForStringDescribingOperation:dieRollString withOperator:operator atRange:lastOperatorRange]; + } } // If not an operation, the top-level term might be a die roll command. @@ -202,90 +265,44 @@ static NSDictionary *_validCharactersDict; { NSMutableDictionary *expression; - // If the operator is at the beginning, this may be negation; we handle that - // case later below. Otherwise, it's a binary operator. - if(operatorRange.location != 0) + expression = [NSMutableDictionary dictionary]; + expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; + expression[SA_DB_INPUT_STRING] = dieRollString; + + // Operands of a binary operator are the expressions generated by + // parsing the strings before and after the addition operator. + expression[SA_DB_OPERAND_LEFT] = [self legacyExpressionForLegalString:[dieRollString substringToIndex:operatorRange.location]]; + expression[SA_DB_OPERAND_RIGHT] = [self legacyExpressionForLegalString:[dieRollString substringFromIndex:(operatorRange.location + operatorRange.length)]]; + + // Check to see if the term is a subtraction operation. + if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_MINUS] containsCharactersInString:operator]) { - expression = [NSMutableDictionary dictionary]; - expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; - expression[SA_DB_INPUT_STRING] = dieRollString; - - // Operands of a binary operator are the expressions generated by - // parsing the strings before and after the addition operator. - expression[SA_DB_OPERAND_LEFT] = [self legacyExpressionForLegalString:[dieRollString substringToIndex:operatorRange.location]]; - expression[SA_DB_OPERAND_RIGHT] = [self legacyExpressionForLegalString:[dieRollString substringFromIndex:(operatorRange.location + operatorRange.length)]]; - - // Check to see if the term is a subtraction operation. - if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_MINUS] containsCharactersInString:operator]) - { - expression[SA_DB_OPERATOR] = SA_DB_OPERATOR_MINUS; - } - // Check to see if the term is an addition operation. - else if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_PLUS] containsCharactersInString:operator]) - { - expression[SA_DB_OPERATOR] = SA_DB_OPERATOR_PLUS; - } - // Check to see if the term is a multiplication operation. - else if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_TIMES] containsCharactersInString:operator]) - { - // Look for other, lower-precedence operators to the left of the - // multiplication operator. If found, split the string there - // instead of at the current operator. - NSString *allLowerPrecedenceOperators = [NSString stringWithFormat:@"%@%@", [SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_PLUS], [SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_MINUS]]; - NSRange lastLowerPrecedenceOperatorRange = [dieRollString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:allLowerPrecedenceOperators] options:NSBackwardsSearch range:NSMakeRange(1, operatorRange.location - 1)]; - if(lastLowerPrecedenceOperatorRange.location != NSNotFound) - { - NSString *lowerPrecedenceOperator = [dieRollString substringWithRange:lastLowerPrecedenceOperatorRange]; - return [self legacyExpressionForStringDescribingOperation:dieRollString withOperator:lowerPrecedenceOperator atRange:lastLowerPrecedenceOperatorRange]; - } - - expression[SA_DB_OPERATOR] = SA_DB_OPERATOR_TIMES; - } - else - { - addErrorToExpression(SA_DB_ERROR_UNKNOWN_OPERATOR, expression); - } + expression[SA_DB_OPERATOR] = SA_DB_OPERATOR_MINUS; + } + // Check to see if the term is an addition operation. + else if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_PLUS] containsCharactersInString:operator]) + { + expression[SA_DB_OPERATOR] = SA_DB_OPERATOR_PLUS; + } + // Check to see if the term is a multiplication operation. + else if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_TIMES] containsCharactersInString:operator]) + { + // Look for other, lower-precedence operators to the left of the + // multiplication operator. If found, split the string there + // instead of at the current operator. + NSString *allLowerPrecedenceOperators = [NSString stringWithFormat:@"%@%@", [SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_PLUS], [SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_MINUS]]; + NSRange lastLowerPrecedenceOperatorRange = [dieRollString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:allLowerPrecedenceOperators] options:NSBackwardsSearch range:NSMakeRange(1, operatorRange.location - 1)]; + if(lastLowerPrecedenceOperatorRange.location != NSNotFound) + { + NSString *lowerPrecedenceOperator = [dieRollString substringWithRange:lastLowerPrecedenceOperatorRange]; + return [self legacyExpressionForStringDescribingOperation:dieRollString withOperator:lowerPrecedenceOperator atRange:lastLowerPrecedenceOperatorRange]; + } + + expression[SA_DB_OPERATOR] = SA_DB_OPERATOR_TIMES; } - // Check to see if the term is a negation operation (we can only reach this - // case if the subtraction operator is at the beginning). else { - if(dieRollString.length == operatorRange.length) - { - expression = [NSMutableDictionary dictionary]; - expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; - expression[SA_DB_INPUT_STRING] = dieRollString; - - addErrorToExpression(SA_DB_ERROR_INVALID_EXPRESSION, expression); - - return expression; - } - if([[SA_DiceParser validCharactersForOperator:SA_DB_OPERATOR_MINUS] containsCharactersInString:operator]) - { - // It turns out this isn't actually an operation expression. It's a - // simple value expression with a negative number. - - // This next line is a hack to account for the fact that Cocoa's - // Unicode compliance is incomplete. :( NSString's integerValue - // method only accepts the hyphen as a negation sign when reading a - // number - not any of the Unicode characters which officially - // symbolize negation! But we are more modern-minded, and accept - // arbitrary symbols as minus-sign. For proper parsing, though, - // we have to replace it like this... - NSString *fixedRollString = [dieRollString stringByReplacingCharactersInRange:operatorRange withString:@"-"]; - - return [self legacyExpressionForStringDescribingNumericValue:fixedRollString]; - } - else - { - expression = [NSMutableDictionary dictionary]; - expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; - expression[SA_DB_INPUT_STRING] = dieRollString; - - addErrorToExpression(SA_DB_ERROR_INVALID_EXPRESSION, expression); - - return expression; - } + addErrorToExpression(SA_DB_ERROR_UNKNOWN_OPERATOR, expression); } // The operands have now been parsed recursively; this parsing may have