| { | { | ||||
| NSString *operator = [dieRollString substringWithRange:lastOperatorRange]; | 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. | // If not an operation, the top-level term might be a die roll command. | ||||
| { | { | ||||
| NSMutableDictionary *expression; | 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 | |||||
| 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) | |||||
| { | { | ||||
| addErrorToExpression(SA_DB_ERROR_UNKNOWN_OPERATOR, expression); | |||||
| 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 | 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 | // The operands have now been parsed recursively; this parsing may have |