Skip to content

Commit

Permalink
Allow [==, value, value, collator]
Browse files Browse the repository at this point in the history
Use collator.compare() if both arguments evaluate to strings, and === otherwise
  • Loading branch information
Anand Thakker committed Jul 23, 2018
1 parent 212844d commit ccb6e1d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 11 deletions.
37 changes: 26 additions & 11 deletions src/style-spec/expression/definitions/comparison.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,21 @@ function gteqCollate(ctx, a, b, c) { return c.compare(a, b) >= 0; }
* @private
*/
function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollator) {
const isOrderComparison = op !== '==' && op !== '!=';

return class Comparison implements Expression {
type: Type;
lhs: Expression;
rhs: Expression;
collator: ?Expression;
needsRuntimeTypeCheck: boolean;
hasUntypedArgument: boolean;

constructor(lhs: Expression, rhs: Expression, collator: ?Expression) {
this.type = BooleanType;
this.lhs = lhs;
this.rhs = rhs;
this.collator = collator;
this.needsRuntimeTypeCheck = op !== '==' && op !== '!=' &&
lhs.type.kind === 'value' && rhs.type.kind === 'value';
this.hasUntypedArgument = lhs.type.kind === 'value' || rhs.type.kind === 'value';
}

static parse(args: Array<mixed>, context: ParsingContext): ?Expression {
Expand All @@ -94,15 +95,15 @@ function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollato
return context.concat(2).error(`"${op}" comparisons are not supported for type '${toString(rhs.type)}'.`);
}

if (!(
lhs.type.kind === rhs.type.kind ||
lhs.type.kind === 'value' ||
rhs.type.kind === 'value'
)) {
if (
lhs.type.kind !== rhs.type.kind &&
lhs.type.kind !== 'value' &&
rhs.type.kind !== 'value'
) {
return context.error(`Cannot compare types '${toString(lhs.type)}' and '${toString(rhs.type)}'.`);
}

if (op !== '==' && op !== '!=') {
if (isOrderComparison) {
// typing rules specific to less/greater than operators
if (lhs.type.kind === 'value' && rhs.type.kind !== 'value') {
// (value, T)
Expand All @@ -115,7 +116,12 @@ function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollato

let collator = null;
if (args.length === 4) {
if (lhs.type.kind !== 'string' && rhs.type.kind !== 'string') {
if (
lhs.type.kind !== 'string' &&
rhs.type.kind !== 'string' &&
lhs.type.kind !== 'value' &&
rhs.type.kind !== 'value'
) {
return context.error(`Cannot use collator to compare non-string types.`);
}
collator = context.parse(args[3], 3, CollatorType);
Expand All @@ -128,7 +134,8 @@ function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollato
evaluate(ctx: EvaluationContext) {
const lhs = this.lhs.evaluate(ctx);
const rhs = this.rhs.evaluate(ctx);
if (this.needsRuntimeTypeCheck) {

if (isOrderComparison && this.hasUntypedArgument) {
const lt = typeOf(lhs);
const rt = typeOf(rhs);
// check that type is string or number, and equal
Expand All @@ -137,6 +144,14 @@ function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollato
}
}

if (this.collator && !isOrderComparison && this.hasUntypedArgument) {
const lt = typeOf(lhs);
const rt = typeOf(rhs);
if (lt.kind !== 'string' || rt.kind !== 'string') {
return compareBasic(ctx, lhs, rhs);
}
}

return this.collator ?
compareWithCollator(ctx, lhs, rhs, this.collator.evaluate(ctx)) :
compareBasic(ctx, lhs, rhs);
Expand Down
31 changes: 31 additions & 0 deletions test/integration/expression-tests/equal/collator-value/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"expression": [
"==",
["get", "x"],
["get", "y"],
["collator", {"case-sensitive": false, "diacritic-sensitive": false}]
],
"inputs": [
[{}, {"properties": {"x": "1", "y": "1"}}],
[{}, {"properties": {"x": "1", "y": "2"}}],
[{}, {"properties": {"x": "1", "y": null}}],
[{}, {"properties": {"x": null, "y": null}}],
[{}, {"properties": {"x": 1, "y": "1"}}],
[{}, {"properties": {"x": 1, "y": 1}}]
],
"expected": {
"compiled": {
"result": "success",
"isFeatureConstant": false,
"isZoomConstant": true,
"type": "boolean"
},
"outputs": [true, false, false, true, false, true],
"serialized": [
"==",
["get", "x"],
["get", "y"],
["collator", {"case-sensitive": false, "diacritic-sensitive": false}]
]
}
}

0 comments on commit ccb6e1d

Please sign in to comment.