Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Install aftermarket expression functions #11472

Merged
merged 23 commits into from
Mar 29, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
09810d5
[ios, macos] Introduced more ergonomic expression concatenation syntax
1ec5 Mar 16, 2018
f10402d
[ios, macos] Fixed mgl_join:
1ec5 Mar 16, 2018
5ce7411
[ios, macos] Simplified expression document headings
1ec5 Mar 17, 2018
3f02c5e
[ios, macos] Documented mgl_join:
1ec5 Mar 17, 2018
38c31dd
[ios, macos] Convert length operator to length: for strings
1ec5 Mar 19, 2018
66648e5
[ios, macos] Implemented type conversion using CAST()
1ec5 Mar 19, 2018
1dcc93d
[ios, macos] Aftermarket let expressions
1ec5 Mar 26, 2018
e467df8
[ios, macos] Refactored aftermarket expression functions
1ec5 Mar 26, 2018
ee3b5a2
[ios, macos] Updated documentation, tests, demo apps
1ec5 Mar 27, 2018
3b79fc9
[ios, macos] Added generic expression function
1ec5 Mar 27, 2018
d84eede
[ios, macos] Add MGL_MATCH function placeholder.
fabian-guerra Mar 27, 2018
ddc7769
[ios, macos] Add MGL_SWITCH expression operator.
fabian-guerra Mar 27, 2018
63d7ebb
[ios, macos] Add mgl_coalesce: as expression function.
fabian-guerra Mar 27, 2018
c06655e
[ios, macos] Update style documentation.
fabian-guerra Mar 28, 2018
74b425f
[ios, macos] Add conventional custom function support.
fabian-guerra Mar 28, 2018
8d19aaa
[ios, macos] Updated example code
1ec5 Mar 28, 2018
79844eb
[ios, macos] Add mgl_coalesce conventional custom function support.
fabian-guerra Mar 28, 2018
0a9b7a0
[ios, macos] Add aftermarket function to 'has' operator.
fabian-guerra Mar 29, 2018
a51ae90
[ios, macos] Add documentation for lookup and feature operators.
fabian-guerra Mar 29, 2018
2625c7c
[ios, macos] Documented simple lookup
1ec5 Mar 29, 2018
72499da
[ios, macos] Renamed, reversed has expression
1ec5 Mar 29, 2018
9c70d38
[ios, macos] Restored OEM conditionals where available
1ec5 Mar 29, 2018
5bb9ca2
[ios, macos] Update style docs.
fabian-guerra Mar 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion platform/darwin/docs/guides/For Style Authors.md.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ In style specification | Method, function, or predicate type | Format string syn
`properties` | |`$mgl_featureProperties`
`at` | `objectFrom:withIndex:` | `array[n]`
`get` | `+[NSExpression expressionForKeyPath:]` | Key path
`has` | `mgl_hasProperty:properties:` |
`has` | `mgl_does:have:` | `mgl_does:have:(self, 'key')`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed and reordered this function. Instead of calling mgl_hasProperty:properties:('key', self), you call mgl_does:have:(self, 'key'). I think this reads better: “Does self have ‘key’?” It’s also consistent with the order when you call the conventional function: FUNCTION(self, 'mgl_has:', 'key').

`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})`
`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)`
`!=` | `NSNotEqualToPredicateOperatorType` | `key != value`
Expand Down
14 changes: 13 additions & 1 deletion platform/darwin/docs/guides/Predicates and Expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ string syntax:

Initializer parameter | Format string syntax | Description
----------------------|----------------------|------------
`mgl_does:have:` | `mgl_does:have:(SELF, 'key')` or `mgl_does:have:(%@, 'key')` | Returns a Boolean value indicating whether the dictionary has a value for the key or whether the evaluated object (`SELF`) has a value for the feature attribute. Compared to the `mgl_has:` custom function, that function’s target is instead passed in as the first argument to this function. Both functions are equivalent to the syntax `key != NIL` or `%@[key] != NIL` but can be used outside of a predicate.
`mgl_interpolate:withCurveType:parameters:stops:` | `mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)` | Produces continuous, smooth results by interpolating between pairs of input and output values (“stops”). Compared to the `mgl_interpolateWithCurveType:parameters:stops:` custom function, the input expression (that function’s target) is instead passed in as the first argument to this function.
`mgl_step:from:stops:` | `mgl_step:from:stops:(x, 11, %@)` |Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of input and output values ("stops"). Compared to the `mgl_stepWithMinimum:stops:` custom function, the input expression (that function’s target) is instead passed in as the first argument to this function.
`mgl_step:from:stops:` | `mgl_step:from:stops:(x, 11, %@)` | Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of input and output values ("stops"). Compared to the `mgl_stepWithMinimum:stops:` custom function, the input expression (that function’s target) is instead passed in as the first argument to this function.
`mgl_join:` | `mgl_join({'Old', 'MacDonald'})` | Returns the result of concatenating together all the elements of an array in order. Compared to the `stringByAppendingString:` custom function, this function takes only one argument, which is an aggregate expression containing the strings to concatenate.
`mgl_coalesce:` | `mgl_coalesce({x, y, z})` | Returns the first non-`nil` value from an array of expressions.
`MGL_LET` | `MGL_LET('age', uppercase('old'), 'name', uppercase('MacDonald'), mgl_join({$age, $name}))` | Any number of variable names interspersed with their assigned `NSExpression` values, followed by an `NSExpression` that may contain references to those variables. Compared to the `mgl_expressionWithContext:` custom function, this function takes the variable names and values inline before the expression that contains references to those variables.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rows need to be added for MGL_MATCH, MGL_SWITCH, and mgl_coalesce:.

Expand Down Expand Up @@ -221,6 +222,17 @@ The following custom functions are also available with the
empty string, 0, `FALSE`, `NIL`, or NaN, otherwise `TRUE`.
</td>
</tr>
<tr>
<td><code>mgl_has:</code></td>
<td>
An `NSExpression` that evaluates to an <code>NSDictionary</code> or the evaluated object (<code>SELF</code>).
</td>
<td>
An `NSExpression` that evaluates to an <code>NSString</code> representing the key to look up in the dictionary or the feature attribute to look up in the evaluated object (see <code>MGLFeature.attributes</code>).
</td>
<td>
`true` if the dictionary has a value for the key or if the evaluated object has a value for the feature attribute.
</td>
<tr>
<td><code>mgl_expressionWithContext:</code></td>
<td>
Expand Down
29 changes: 13 additions & 16 deletions platform/darwin/src/NSExpression+MGLAdditions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ + (void)installFunctions {
INSTALL_METHOD(mgl_interpolate:withCurveType:parameters:stops:);
INSTALL_METHOD(mgl_step:from:stops:);
INSTALL_METHOD(mgl_coalesce:);
INSTALL_METHOD(mgl_hasProperty:properties:);
INSTALL_METHOD(mgl_does:have:);

// Install functions that resemble control structures, taking arbitrary
// numbers of arguments. Vararg aftermarket functions need to be declared
Expand Down Expand Up @@ -111,12 +111,11 @@ - (id)mgl_coalesce:(NSArray<NSExpression *> *)elements {
}

/**
A placeholder for a method that evaluates has expression.
Returns a Boolean value indicating whether the object has a value for the given
key.
*/
- (id)mgl_hasProperty:(id)element properties:(id)properties {
[NSException raise:NSInvalidArgumentException
format:@"Has expressions lack underlying Objective-C implementations."];
return nil;
- (BOOL)mgl_does:(id)object have:(NSString *)key {
return [object valueForKey:key] != nil;
}

/**
Expand Down Expand Up @@ -594,10 +593,9 @@ + (instancetype)mgl_expressionWithJSONObject:(id)object {
return [NSExpression expressionForFunction:@"objectFrom:withIndex:" arguments:@[operand, index]];
} else if ([op isEqualToString:@"has"]) {
NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
NSExpression *operand = argumentObjects.count > 1 ? subexpressions[1] : [NSExpression expressionWithFormat:@"self"];
NSExpression *property = subexpressions.firstObject;

return [NSExpression expressionForFunction:@"mgl_hasProperty:properties:" arguments:@[property, operand]];
NSExpression *operand = argumentObjects.count > 1 ? subexpressions[1] : [NSExpression expressionForEvaluatedObject];
NSExpression *key = subexpressions.firstObject;
return [NSExpression expressionForFunction:@"mgl_does:have:" arguments:@[operand, key]];
} else if ([op isEqualToString:@"interpolate"]) {
NSArray *interpolationOptions = argumentObjects.firstObject;
NSString *curveType = interpolationOptions.firstObject;
Expand Down Expand Up @@ -848,7 +846,7 @@ - (id)mgl_jsonExpressionObject {
return @[@"to-string", self.operand.mgl_jsonExpressionObject];
} else if ([function isEqualToString:@"noindex:"]) {
return self.arguments.firstObject.mgl_jsonExpressionObject;
} else if ([function isEqualToString:@"mgl_hasProperty:properties:"] ||
} else if ([function isEqualToString:@"mgl_does:have:"] ||
[function isEqualToString:@"mgl_has:"]) {
return self.mgl_jsonHasExpressionObject;
} else if ([function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"]
Expand Down Expand Up @@ -1075,16 +1073,15 @@ - (id)mgl_jsonCoalesceExpressionObject {
}

- (id)mgl_jsonHasExpressionObject {
BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_hasProperty:properties:"];
NSExpression *operand = isAftermarketFunction ? self.arguments[1] : self.operand;
BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_does:have:"];
NSExpression *operand = isAftermarketFunction ? self.arguments[0] : self.operand;
NSExpression *key = self.arguments[isAftermarketFunction ? 1 : 0];

NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"has", self.arguments[0].mgl_jsonExpressionObject, nil];
NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"has", key.mgl_jsonExpressionObject, nil];
if (operand.expressionType != NSEvaluatedObjectExpressionType) {
[expressionObject addObject:operand.mgl_jsonExpressionObject];
}
return expressionObject;

return expressionObject;
}

@end
15 changes: 9 additions & 6 deletions platform/darwin/test/MGLExpressionTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -720,24 +720,27 @@ - (void)testLookupExpressionObject {
XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
}
{
NSExpression *expression = [NSExpression expressionForFunction:@"mgl_hasProperty:properties:" arguments:@[[NSExpression expressionForConstantValue:@"x"],
[NSExpression expressionForEvaluatedObject]]];
NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
arguments:@[[NSExpression expressionForEvaluatedObject],
[NSExpression expressionForConstantValue:@"x"]]];
NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(self, 'mgl_has:', 'x')"];
NSArray *jsonExpression = @[@"has", @"x"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
}
{
NSExpression *expression = [NSExpression expressionForFunction:@"mgl_hasProperty:properties:" arguments:@[[NSExpression expressionForConstantValue:@"x"],
[NSExpression expressionForConstantValue:@{@"x": MGLConstantExpression(@0)}]]];
NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
arguments:@[MGLConstantExpression(@{@"x": MGLConstantExpression(@0)}),
MGLConstantExpression(@"x")]];
NSArray *jsonExpression = @[@"has", @"x", @[@"literal", @{@"x": @0}]];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
}
{
NSExpression *expression = [NSExpression expressionForFunction:@"mgl_hasProperty:properties:" arguments:@[[NSExpression expressionForConstantValue:@"x"],
[NSExpression expressionForVariable:@"mgl_featureProperties"]]];
NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
arguments:@[[NSExpression expressionForVariable:@"mgl_featureProperties"],
[NSExpression expressionForConstantValue:@"x"]]];
NSArray *jsonExpression = @[@"has", @"x", @[@"properties"]];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
Expand Down