| // No overflow will occur. We can perform the subtraction operation. | // No overflow will occur. We can perform the subtraction operation. | ||||
| result[SA_DB_RESULT] = @(leftOperand - rightOperand); | result[SA_DB_RESULT] = @(leftOperand - rightOperand); | ||||
| // Return the successfully evaluated negation expression. | |||||
| // Return the successfully evaluated subtraction expression. | |||||
| return result; | return result; | ||||
| } | } | ||||
| // Check to see if the operation is addition. | // Check to see if the operation is addition. |
| // listed below. | // listed below. | ||||
| // Operation expressions also have a value for the SA_DB_OPERAND_RIGHT key, | // Operation expressions also have a value for the SA_DB_OPERAND_RIGHT key, | ||||
| // and, possibly, a value for the SA_DB_OPERAND_LEFT key as well. | // and, possibly, a value for the SA_DB_OPERAND_LEFT key as well. | ||||
| // (An operation expression with an SA_DB_OPERATOR value of SA_DB_OPERATOR_MINUS | |||||
| // that does not have an SA_DB_OPERAND_LEFT value is simply a negation of the | |||||
| // SA_DB_OPERAND_RIGHT value.) | |||||
| // The values for the SA_DB_OPERAND_RIGHT (and, if present, SA_DB_OPERAND_LEFT) | // The values for the SA_DB_OPERAND_RIGHT (and, if present, SA_DB_OPERAND_LEFT) | ||||
| // are themselves expressions of some type (i.e. NSDictionary objects), which | // are themselves expressions of some type (i.e. NSDictionary objects), which | ||||
| // must be recursively evaluated. | // must be recursively evaluated. | ||||
| // Terms of type SA_DB_TERM_TYPE_VALUE (a.k.a. simple value expressions) have a | // Terms of type SA_DB_TERM_TYPE_VALUE (a.k.a. simple value expressions) have a | ||||
| // value for the SA_DB_VALUE key, which is an NSNumber that represents an | // value for the SA_DB_VALUE key, which is an NSNumber that represents an | ||||
| // NSInteger value. | // NSInteger value. | ||||
| // NOTE: Despite being an NSInteger, this numeric value may not be negative. | |||||
| extern NSString * const SA_DB_VALUE; // key | extern NSString * const SA_DB_VALUE; // key | ||||
| // All terms that were generated via parsing a string should have a value for | // All terms that were generated via parsing a string should have a value for | ||||
| extern NSString * const SA_DB_STRING_FORMAT_RULES_PLIST_NAME; | extern NSString * const SA_DB_STRING_FORMAT_RULES_PLIST_NAME; | ||||
| /* | /* | ||||
| The string format rules file (whose filename - minus the .plist extension - | |||||
| is given by the SA_DB_STRING_FORMAT_RULES_PLIST_NAME string) contains | |||||
| values for variables that define the properties of legal die roll strings, | |||||
| as well as certain variables that define the format of result strings. | |||||
| The string format rules file (whose basename (i.e., filename minus the .plist | |||||
| extension) is given by the SA_DB_STRING_FORMAT_RULES_PLIST_NAME string) | |||||
| contains values for variables that define the properties of legal die roll | |||||
| strings, as well as certain variables that define the format of result strings. | |||||
| The file is organized as a dictionary contaning several sub-dictionaries. The | The file is organized as a dictionary contaning several sub-dictionaries. The | ||||
| valid keys (those for which values are present in the file), and the values | valid keys (those for which values are present in the file), and the values |
| - (NSString *)stringFromExpression:(NSDictionary *)expression; | - (NSString *)stringFromExpression:(NSDictionary *)expression; | ||||
| - (NSAttributedString *)attributedStringFromExpression:(NSDictionary *)expression; | - (NSAttributedString *)attributedStringFromExpression:(NSDictionary *)expression; | ||||
| + (NSString *)canonicalRepresentationForOperator:(NSString *)operatorName; | |||||
| @end | @end |
| - (NSDictionary *)expressionForString:(NSString *)dieRollString; | - (NSDictionary *)expressionForString:(NSString *)dieRollString; | ||||
| - (NSDictionary *)expressionByJoiningExpression:(NSDictionary *)leftHandExpression toExpression:(NSDictionary *)rightHandExpression withOperator:(NSString *)operatorName; | |||||
| @end | @end |
| #import "SA_DiceExpressionStringConstants.h" | #import "SA_DiceExpressionStringConstants.h" | ||||
| #import "SA_DiceErrorHandling.h" | #import "SA_DiceErrorHandling.h" | ||||
| #import "NSString+SA_NSStringExtensions.h" | #import "NSString+SA_NSStringExtensions.h" | ||||
| #import "SA_DiceFormatter.h" | |||||
| /********************************/ | /********************************/ | ||||
| #pragma mark File-scope variables | #pragma mark File-scope variables | ||||
| } | } | ||||
| } | } | ||||
| - (NSDictionary *)expressionByJoiningExpression:(NSDictionary *)leftHandExpression toExpression:(NSDictionary *)rightHandExpression withOperator:(NSString *)operatorName | |||||
| { | |||||
| NSMutableDictionary *expression = [NSMutableDictionary dictionary]; | |||||
| // First, we check that the operands and operator are not nil. If they are, | |||||
| // then the expression is invalid... | |||||
| if(leftHandExpression == nil || rightHandExpression == nil || operatorName == nil) | |||||
| { | |||||
| expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_NONE; | |||||
| addErrorToExpression(SA_DB_ERROR_INVALID_EXPRESSION, expression); | |||||
| return expression; | |||||
| } | |||||
| // If the operands and operator are present, then the expression is an | |||||
| // operation expression... | |||||
| expression[SA_DB_TERM_TYPE] = SA_DB_TERM_TYPE_OPERATION; | |||||
| // ... but does it have a valid operator? | |||||
| if([operatorName isEqualToString:SA_DB_OPERATOR_PLUS] || | |||||
| [operatorName isEqualToString:SA_DB_OPERATOR_MINUS] || | |||||
| [operatorName isEqualToString:SA_DB_OPERATOR_TIMES] | |||||
| ) | |||||
| { | |||||
| expression[SA_DB_OPERATOR] = operatorName; | |||||
| } | |||||
| else | |||||
| { | |||||
| addErrorToExpression(SA_DB_ERROR_UNKNOWN_OPERATOR, expression); | |||||
| return expression; | |||||
| } | |||||
| // The operator is valid. Set the operands... | |||||
| expression[SA_DB_OPERAND_LEFT] = leftHandExpression; | |||||
| expression[SA_DB_OPERAND_RIGHT] = rightHandExpression; | |||||
| // And inherit any errors that they may have. | |||||
| addErrorsFromExpressionToExpression(expression[SA_DB_OPERAND_LEFT], expression); | |||||
| addErrorsFromExpressionToExpression(expression[SA_DB_OPERAND_RIGHT], expression); | |||||
| // Since this top-level expression was NOT generated by parsing an input | |||||
| // string, for completeness and consistency, we have to generate a fake | |||||
| // input string ourselves! We do this by wrapping each operand in | |||||
| // parentheses and putting the canonical representation of the operator | |||||
| // between them. | |||||
| NSString *fakeInputString = [NSString stringWithFormat:@"(%@)%@(%@)", | |||||
| expression[SA_DB_OPERAND_LEFT][SA_DB_INPUT_STRING], | |||||
| [SA_DiceFormatter canonicalRepresentationForOperator:expression[SA_DB_OPERATOR]], | |||||
| expression[SA_DB_OPERAND_RIGHT][SA_DB_INPUT_STRING]]; | |||||
| expression[SA_DB_INPUT_STRING] = fakeInputString; | |||||
| // The joining is complete. (Power overwhelming.) | |||||
| return expression; | |||||
| } | |||||
| /**********************************************/ | /**********************************************/ | ||||
| #pragma mark - "Legacy" behavior implementation | #pragma mark - "Legacy" behavior implementation | ||||
| /**********************************************/ | /**********************************************/ | ||||
| // First, we check for whether there even is anything more to the | // First, we check for whether there even is anything more to the | ||||
| // roll string besides the operator. If not, then the string is | // roll string besides the operator. If not, then the string is | ||||
| // definitely malformed... | |||||
| // malformed by definition... | |||||
| if(dieRollString.length == lastOperatorRange.length) | if(dieRollString.length == lastOperatorRange.length) | ||||
| { | { | ||||
| expression = [NSMutableDictionary dictionary]; | expression = [NSMutableDictionary dictionary]; |