Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Parity with the old term system

  • Loading branch information...
commit 33bbe840ec9b7cf5db417ba8bf1f0d3b42f2c34a 1 parent 74d8f6d
@davedelong authored
View
9 DDMathParser/DDMathStringToken.h
@@ -19,10 +19,11 @@
+ (id) mathStringTokenWithToken:(NSString *)t type:(DDTokenType)type;
-@property (readonly) NSString * token;
-@property (readonly) DDTokenType tokenType;
-@property (readonly) DDOperator operatorType;
-@property DDPrecedence operatorPrecedence;
+@property (nonatomic,readonly) NSString * token;
+@property (nonatomic,readonly) DDTokenType tokenType;
+@property (nonatomic,readonly) DDOperator operatorType;
+@property (nonatomic,readonly) DDOperatorArity operatorArity;
+@property (nonatomic) DDPrecedence operatorPrecedence;
- (NSNumber *) numberValue;
View
25 DDMathParser/DDMathStringToken.m
@@ -9,7 +9,7 @@
#import "DDMathStringToken.h"
@implementation DDMathStringToken
-@synthesize token, tokenType, operatorType, operatorPrecedence;
+@synthesize token, tokenType, operatorType, operatorPrecedence, operatorArity;
- (void) dealloc {
[token release];
@@ -24,27 +24,33 @@ - (id) initWithToken:(NSString *)t type:(DDTokenType)type {
tokenType = type;
operatorType = DDOperatorInvalid;
operatorPrecedence = DDPrecedenceNone;
+ operatorArity = DDOperatorArityUnknown;
if (tokenType == DDTokenTypeOperator) {
if ([token isEqual:@"|"]) {
operatorType = DDOperatorBitwiseOr;
operatorPrecedence = DDPrecedenceBitwiseOr;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"^"]) {
operatorType = DDOperatorBitwiseXor;
operatorPrecedence = DDPrecedenceBitwiseXor;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"&"]) {
operatorType = DDOperatorBitwiseAnd;
operatorPrecedence = DDPrecedenceBitwiseAnd;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"<<"]) {
operatorType = DDOperatorLeftShift;
operatorPrecedence = DDPrecedenceLeftShift;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@">>"]) {
operatorType = DDOperatorRightShift;
operatorPrecedence = DDPrecedenceRightShift;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"-"]) {
operatorType = DDOperatorMinus;
@@ -57,26 +63,32 @@ - (id) initWithToken:(NSString *)t type:(DDTokenType)type {
if ([token isEqual:@"/"]) {
operatorType = DDOperatorDivide;
operatorPrecedence = DDPrecedenceDivision;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"*"]) {
operatorType = DDOperatorMultiply;
operatorPrecedence = DDPrecedenceMultiplication;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"%"]) {
operatorType = DDOperatorModulo;
operatorPrecedence = DDPrecedenceModulo;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"~"]) {
operatorType = DDOperatorBitwiseNot;
operatorPrecedence = DDPrecedenceUnary;
+ operatorArity = DDOperatorArityUnary;
}
if ([token isEqual:@"!"]) {
operatorType = DDOperatorFactorial;
operatorPrecedence = DDPrecedenceFactorial;
+ operatorArity = DDOperatorArityUnary;
}
if ([token isEqual:@"**"]) {
operatorType = DDOperatorPower;
operatorPrecedence = DDPrecedencePower;
+ operatorArity = DDOperatorArityBinary;
}
if ([token isEqual:@"("]) {
operatorType = DDOperatorParenthesisOpen;
@@ -128,4 +140,15 @@ - (DDOperator) operatorType {
return operatorType;
}
+- (void)setOperatorPrecedence:(DDPrecedence)precedence {
+ if (operatorArity == DDOperatorArityUnknown) {
+ if (precedence == DDPrecedenceUnary || precedence == DDPrecedenceFactorial) {
+ operatorArity = DDOperatorArityUnary;
+ } else if (precedence != DDPrecedenceNone) {
+ operatorArity = DDOperatorArityBinary;
+ }
+ }
+ operatorPrecedence = precedence;
+}
+
@end
View
9 DDMathParser/DDParser.m
@@ -122,6 +122,7 @@ - (void) dealloc {
- (DDOperatorAssociativity) associativityForOperator:(DDOperator)operatorType {
switch (operatorType) {
+ // binary operators can have customizable associativity
case DDOperatorBitwiseOr: return bitwiseOrAssociativity;
case DDOperatorBitwiseXor: return bitwiseXorAssociativity;
case DDOperatorBitwiseAnd: return bitwiseAndAssociativity;
@@ -134,8 +135,14 @@ - (DDOperatorAssociativity) associativityForOperator:(DDOperator)operatorType {
case DDOperatorModulo: return modAssociativity;
case DDOperatorPower: return powerAssociativity;
- //unary operators are right associative (factorial doesn't really count)
+ // unary operators are always right associative (except for factorial)
+ case DDOperatorUnaryPlus:
+ case DDOperatorUnaryMinus:
case DDOperatorBitwiseNot: return DDOperatorAssociativityRight;
+
+ // factorial is always left associative
+ case DDOperatorFactorial: return DDOperatorAssociativityLeft;
+
default: return DDOperatorAssociativityLeft;
}
return DDOperatorAssociativityLeft;
View
7 DDMathParser/DDParserTypes.h
@@ -44,6 +44,13 @@ typedef enum {
DDOperatorUnaryPlus
} DDOperator;
+typedef enum {
+ DDOperatorArityUnknown = 0,
+
+ DDOperatorArityUnary,
+ DDOperatorArityBinary
+} DDOperatorArity;
+
enum {
DDPrecedenceBitwiseOr = 0,
DDPrecedenceBitwiseXor,
View
2  DDMathParser/_DDFunctionTerm.h
@@ -12,4 +12,6 @@
@property (nonatomic,readonly) NSString *functionName;
+- (id)_initWithFunction:(NSString *)function subterms:(NSArray *)terms error:(NSError **)error;
+
@end
View
59 DDMathParser/_DDFunctionTerm.m
@@ -12,9 +12,19 @@
#import "DDMathParserMacros.h"
#import "_DDOperatorTerm.h"
+#import "DDExpression.h"
+
@implementation _DDFunctionTerm
@synthesize functionName;
+- (id)_initWithFunction:(NSString *)function subterms:(NSArray *)terms error:(NSError **)error {
+ self = [super _initWithSubterms:terms error:error];
+ if (self) {
+ functionName = [function copy];
+ }
+ return self;
+}
+
- (id)_initWithTokenizer:(DDMathStringTokenizer *)tokenizer error:(NSError **)error {
DDMathStringToken *token = [tokenizer nextToken];
@@ -29,18 +39,37 @@ - (id)_initWithTokenizer:(DDMathStringTokenizer *)tokenizer error:(NSError **)er
if ([term type] == DDParserTermTypeOperator && [(_DDOperatorTerm *)term operatorType] == DDOperatorComma) {
NSArray *parameterGroupTerms = [[self subterms] subarrayWithRange:subrange];
- NSError *error = nil;
- _DDGroupTerm *parameterGroup = [[_DDGroupTerm alloc] _initWithSubterms:parameterGroupTerms error:&error];
- if (parameterGroup) {
- [newSubterms addObject:parameterGroup];
+ if ([parameterGroupTerms count] != 1) {
+ _DDGroupTerm *parameterGroup = [[_DDGroupTerm alloc] _initWithSubterms:parameterGroupTerms error:error];
+ if (parameterGroup) {
+ [newSubterms addObject:parameterGroup];
+ }
+ [parameterGroup release];
+ } else {
+ // there's only one term in this parameter; no need to group it in parentheses
+ [newSubterms addObject:[parameterGroupTerms objectAtIndex:0]];
}
- [parameterGroup release];
+
subrange.location = NSMaxRange(subrange)+1;
subrange.length = 0;
} else {
subrange.length++;
}
}
+
+ // get the last parameter
+ NSRange rangeOfLastParameter;
+ rangeOfLastParameter.location = subrange.location;
+ rangeOfLastParameter.length = [[self subterms] count]-rangeOfLastParameter.location;
+ if (rangeOfLastParameter.length > 1) {
+ NSArray *lastParameters = [[self subterms] subarrayWithRange:rangeOfLastParameter];
+ _DDGroupTerm *parameterGroup = [[_DDGroupTerm alloc] _initWithSubterms:lastParameters error:error];
+ [newSubterms addObject:parameterGroup];
+ [parameterGroup release];
+ } else {
+ [newSubterms addObject:[[self subterms] objectAtIndex:rangeOfLastParameter.location]];
+ }
+
[self _setSubterms:newSubterms];
}
return self;
@@ -53,6 +82,12 @@ - (void)dealloc {
- (DDParserTermType)type { return DDParserTermTypeFunction; }
+- (NSString *)description {
+ NSArray *parameterDescriptions = [[self subterms] valueForKey:@"description"];
+ NSString *parameters = [parameterDescriptions componentsJoinedByString:@","];
+ return [NSString stringWithFormat:@"%@(%@)", functionName, parameters];
+}
+
- (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
if ([self isResolved]) { return YES; }
@@ -66,4 +101,18 @@ - (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
return YES;
}
+- (DDExpression *)expressionWithError:(NSError **)error {
+ ERR_ASSERT(error);
+
+ NSMutableArray *parameters = [NSMutableArray array];
+ for (_DDParserTerm *term in [self subterms]) {
+ DDExpression *parameter = [term expressionWithError:error];
+ if (!parameter) { return nil; }
+
+ [parameters addObject:parameter];
+ }
+
+ return [DDExpression functionExpressionWithFunction:functionName arguments:parameters error:error];
+}
+
@end
View
171 DDMathParser/_DDGroupTerm.m
@@ -11,7 +11,18 @@
#import "DDMathStringToken.h"
#import "DDMathParserMacros.h"
+#import "DDParser.h"
#import "_DDOperatorTerm.h"
+#import "_DDFunctionTerm.h"
+
+@interface _DDGroupTerm ()
+
+- (NSIndexSet *)_indicesOfOperatorsWithHighestPrecedence;
+- (BOOL)_reduceTermsAroundOperatorAtIndex:(NSUInteger)index withParser:(DDParser *)parser error:(NSError **)error;
+- (BOOL)_reduceBinaryOperatorAtIndex:(NSUInteger)index withParser:(DDParser *)parser error:(NSError **)error;
+- (BOOL)_reduceUnaryOperatorAtIndex:(NSUInteger)index withParser:(DDParser *)parser error:(NSError **)error;
+
+@end
@implementation _DDGroupTerm
@synthesize subterms;
@@ -66,9 +77,56 @@ - (void)dealloc {
- (DDParserTermType)type { return DDParserTermTypeGroup; }
+- (NSString *)description {
+ NSArray *descriptions = [[self subterms] valueForKey:@"description"];
+ NSString *description = [descriptions componentsJoinedByString:@""];
+ return [NSString stringWithFormat:@"(%@)", description];
+}
+
#pragma mark - Resolution
-- (NSIndexSet *) indicesOfOperatorsWithHighestPrecedence {
+- (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
+ if ([self isResolved]) { return YES; }
+
+ while ([[self subterms] count] > 1) {
+ NSIndexSet *operatorIndices = [self _indicesOfOperatorsWithHighestPrecedence];
+ if ([operatorIndices count] > 0) {
+ NSUInteger index = [operatorIndices firstIndex];
+ if ([operatorIndices count] > 1) {
+ // we have more than one index
+ // use a different index if the operator is right associative
+ _DDOperatorTerm *operatorTerm = [[self subterms] objectAtIndex:index];
+
+ if ([parser associativityForOperator:[operatorTerm operatorType]] == DDOperatorAssociativityRight) {
+ index = [operatorIndices lastIndex];
+ }
+ }
+
+ // we have the index for the next operator
+ if (![self _reduceTermsAroundOperatorAtIndex:index withParser:parser error:error]) {
+ return NO;
+ }
+
+ } else {
+ // more than one term is left
+ // but there are no more operators
+ *error = ERR_BADARG(@"invalid format: %@", self);
+ return NO;
+ }
+ }
+
+ if ([[self subterms] count] > 0) {
+ _DDParserTerm *subterm = [[self subterms] objectAtIndex:0];
+ if (![subterm resolveWithParser:parser error:error]) {
+ return NO;
+ }
+ }
+
+ [self setResolved:YES];
+ return YES;
+}
+
+- (NSIndexSet *)_indicesOfOperatorsWithHighestPrecedence {
NSMutableIndexSet * indices = [NSMutableIndexSet indexSet];
__block DDPrecedence currentPrecedence = DDPrecedenceUnknown;
[[self subterms] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
@@ -87,10 +145,115 @@ - (NSIndexSet *) indicesOfOperatorsWithHighestPrecedence {
return indices;
}
-- (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
- if ([self isResolved]) { return YES; }
+- (BOOL)_reduceTermsAroundOperatorAtIndex:(NSUInteger)index withParser:(DDParser *)parser error:(NSError **)error {
+ _DDOperatorTerm *operator = [[self subterms] objectAtIndex:index];
+
+ if ([operator operatorArity] == DDOperatorArityBinary) {
+ return [self _reduceBinaryOperatorAtIndex:index withParser:parser error:error];
+ } else if ([operator operatorArity] == DDOperatorArityUnary) {
+ return [self _reduceUnaryOperatorAtIndex:index withParser:parser error:error];
+ }
+
+ *error = ERR_BADARG(@"unknown arity for operator: %@", operator);
+ return NO;
+}
+
+- (BOOL)_reduceBinaryOperatorAtIndex:(NSUInteger)index withParser:(DDParser *)parser error:(NSError **)error {
+#pragma unused(parser)
+ _DDOperatorTerm *operator = [[self subterms] objectAtIndex:index];
+
+ if (index == 0) {
+ *error = ERR_BADARG(@"no left operand to binary %@", operator);
+ return NO;
+ }
+ if (index == [[self subterms] count] - 1) {
+ *error = ERR_BADARG(@"no right operand to binary %@", operator);
+ return NO;
+ }
- //TODO: do the magic
+ NSRange replacementRange = NSMakeRange(index-1, 3);
+
+ _DDParserTerm *leftOperand = [[self subterms] objectAtIndex:index-1];
+ _DDParserTerm *rightmostOperand = [[self subterms] objectAtIndex:index+1];
+
+ NSRange rightOperandRange = NSMakeRange(index+1, 1);
+ while ([rightmostOperand type] == DDParserTermTypeOperator && [(_DDOperatorTerm *)rightmostOperand operatorArity] == DDOperatorArityUnary) {
+ // this should really only happen when operator is the power operator and the exponent has 1+ negations
+ rightOperandRange.length++;
+ if (NSMaxRange(rightOperandRange)-1 >= [[self subterms] count]) {
+ *error = ERR_BADARG(@"no right operand to unary %@", rightmostOperand);
+ return NO;
+ }
+ rightmostOperand = [[self subterms] objectAtIndex:NSMaxRange(rightOperandRange)-1];
+ }
+
+ if (rightOperandRange.length > 1) {
+ NSArray *rightOperands = [[self subterms] subarrayWithRange:rightOperandRange];
+ _DDGroupTerm *group = [[_DDGroupTerm alloc] _initWithSubterms:rightOperands error:error];
+ [[self subterms] replaceObjectsInRange:rightOperandRange withObjectsFromArray:[NSArray arrayWithObject:group]];
+ [group release];
+
+ rightmostOperand = [[self subterms] objectAtIndex:NSMaxRange(replacementRange)-1];
+ }
+
+ NSArray *parameters = [NSArray arrayWithObjects:leftOperand, rightmostOperand, nil];
+ _DDFunctionTerm *function = [[_DDFunctionTerm alloc] _initWithFunction:[operator operatorFunction] subterms:parameters error:error];
+
+ [[self subterms] replaceObjectsInRange:replacementRange withObjectsFromArray:[NSArray arrayWithObject:function]];
+ [function release];
+
+ return YES;
+}
+
+- (BOOL)_reduceUnaryOperatorAtIndex:(NSUInteger)index withParser:(DDParser *)parser error:(NSError **)error {
+ _DDOperatorTerm *operator = [[self subterms] objectAtIndex:index];
+ DDOperatorAssociativity associativity = [parser associativityForOperator:[operator operatorType]];
+
+ NSRange replacementRange = NSMakeRange(0, 0);
+ _DDParserTerm *parameter = nil;
+
+ if (associativity == DDOperatorAssociativityRight) {
+ // right associative unary operator (negate, not)
+ if (index == [[self subterms] count] - 1) {
+ *error = ERR_BADARG(@"no right operand to unary %@", operator);
+ return NO;
+ }
+
+ parameter = [[self subterms] objectAtIndex:index+1];
+ replacementRange = NSMakeRange(index, 2);
+
+ } else {
+ // left associative unary operator (factorial)
+ if (index == 0) {
+ *error = ERR_BADARG(@"no left operand to unary %@", operator);
+ return NO;
+ }
+
+ parameter = [[self subterms] objectAtIndex:index-1];
+ replacementRange = NSMakeRange(index-1, 2);
+
+ }
+
+ NSArray *parameters = [NSArray arrayWithObject:parameter];
+ _DDFunctionTerm *function = [[_DDFunctionTerm alloc] _initWithFunction:[operator operatorFunction] subterms:parameters error:error];
+
+ [[self subterms] replaceObjectsInRange:replacementRange withObjectsFromArray:[NSArray arrayWithObject:function]];
+
+ [function release];
+
+ return YES;
+}
+
+#pragma mark - Expressions
+
+- (DDExpression *)expressionWithError:(NSError **)error {
+ ERR_ASSERT(error);
+ if ([[self subterms] count] == 1) {
+ _DDParserTerm *term = [[self subterms] objectAtIndex:0];
+ return [term expressionWithError:error];
+ }
+ *error = ERR_GENERIC(@"Unable to create expression from term: %@", self);
+ return nil;
}
@end
View
11 DDMathParser/_DDNumberTerm.m
@@ -7,6 +7,9 @@
//
#import "_DDNumberTerm.h"
+#import "DDMathStringToken.h"
+#import "DDExpression.h"
+#import "DDMathParserMacros.h"
@implementation _DDNumberTerm
@@ -16,5 +19,13 @@ - (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
[self setResolved:YES];
return YES;
}
+- (NSString *)description {
+ return [[self token] description];
+}
+
+- (DDExpression *)expressionWithError:(NSError **)error {
+ ERR_ASSERT(error);
+ return [DDExpression numberExpressionWithNumber:[[self token] numberValue]];
+}
@end
View
2  DDMathParser/_DDOperatorTerm.h
@@ -13,5 +13,7 @@
@property (nonatomic,readonly) DDOperator operatorType;
@property (nonatomic,readonly) DDPrecedence operatorPrecedence;
+@property (nonatomic,readonly) DDOperatorArity operatorArity;
+@property (nonatomic,readonly) NSString *operatorFunction;
@end
View
39 DDMathParser/_DDOperatorTerm.m
@@ -21,4 +21,43 @@ - (DDPrecedence)operatorPrecedence {
return [[self token] operatorPrecedence];
}
+- (DDOperatorArity)operatorArity {
+ return [[self token] operatorArity];
+}
+
+- (NSString *)operatorFunction {
+
+ static NSDictionary *operatorNames = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ operatorNames = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"or", @"|",
+ @"xor", @"^",
+ @"and", @"&",
+ @"lshift", @"<<",
+ @"rshift", @">>",
+ @"subtract", @"-",
+ @"add", @"+",
+ @"divide", @"/",
+ @"multiply", @"*",
+ @"mod", @"%",
+ @"not", @"~",
+ @"factorial", @"!",
+ @"pow", @"**",
+ nil];
+ });
+
+ NSString * function = [operatorNames objectForKey:[[self token] token]];
+ if ([self operatorPrecedence] == DDPrecedenceUnary) {
+ if ([[[self token] token] isEqual:@"-"]) { return @"negate"; }
+ if ([[[self token] token] isEqual:@"+"]) { return @""; }
+ }
+
+ return function;
+}
+
+- (NSString *)description {
+ return [[self token] token];
+}
+
@end
View
2  DDMathParser/_DDParserTerm.h
@@ -11,6 +11,7 @@
@class DDMathStringToken;
@class DDMathStringTokenizer;
@class DDParser;
+@class DDExpression;
typedef enum {
DDParserTermTypeNumber = 1,
@@ -31,5 +32,6 @@ typedef enum {
- (id)_initWithTokenizer:(DDMathStringTokenizer *)tokenizer error:(NSError **)error;
- (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error;
+- (DDExpression *)expressionWithError:(NSError **)error;
@end
View
5 DDMathParser/_DDParserTerm.m
@@ -98,4 +98,9 @@ - (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
return NO;
}
+- (DDExpression *)expressionWithError:(NSError **)error {
+ *error = ERR_GENERIC(@"Subclasses must override the -%@ method", NSStringFromSelector(_cmd));
+ return nil;
+}
+
@end
View
11 DDMathParser/_DDVariableTerm.m
@@ -7,6 +7,9 @@
//
#import "_DDVariableTerm.h"
+#import "DDMathParserMacros.h"
+#import "DDMathStringToken.h"
+#import "DDExpression.h"
@implementation _DDVariableTerm
@@ -16,5 +19,13 @@ - (BOOL)resolveWithParser:(DDParser *)parser error:(NSError **)error {
[self setResolved:YES];
return YES;
}
+- (NSString *)description {
+ return [NSString stringWithFormat:@"$%@", [[self token] token]];
+}
+
+- (DDExpression *)expressionWithError:(NSError **)error {
+ ERR_ASSERT(error);
+ return [DDExpression variableExpressionWithVariable:[[self token] token]];
+}
@end
View
13 main.m
@@ -1,6 +1,7 @@
#import <Foundation/Foundation.h>
#import "DDMathParser.h"
#import "ConstantRecognizer.h"
+#import "_DDParserTerm.h"
NSString* readLine() {
@@ -33,6 +34,8 @@ int main (int argc, const char * argv[]) {
printf("\nStandard operators available: + - * / %% ! & | ~ ^ << >>\n");
printf("\nType \"list\" to show available functions\n");
printf("Type \"exit\" to quit\n");
+
+ [DDParser setDefaultPowerAssociativity:DDOperatorAssociativityRight];
NSString * line = nil;
do {
@@ -44,6 +47,16 @@ int main (int argc, const char * argv[]) {
NSError *error = nil;
DDMathStringTokenizer *tokenizer = [[ConstantRecognizer alloc] initWithString:line error:&error];
DDParser *parser = [DDParser parserWithTokenizer:tokenizer error:&error];
+
+ _DDParserTerm *term = [_DDParserTerm rootTermWithTokenizer:tokenizer error:&error];
+ NSLog(@"before: %@", term);
+ [term resolveWithParser:parser error:&error];
+ NSLog(@"after: %@", term);
+ DDExpression *newExpression = [term expressionWithError:&error];
+ NSLog(@"exp: %@", newExpression);
+ NSNumber *newValue = [newExpression evaluateWithSubstitutions:nil evaluator:nil error:&error];
+ NSLog(@"val: %@", newValue);
+
DDExpression *expression = [DDExpression expressionWithParser:parser error:&error];
NSNumber *value = [expression evaluateWithSubstitutions:nil evaluator:nil error:&error];
Please sign in to comment.
Something went wrong with that request. Please try again.