Skip to content

Commit

Permalink
Merged in & index-of expressions to the same class
Browse files Browse the repository at this point in the history
  • Loading branch information
lbutler committed Mar 24, 2020
1 parent 7a2f901 commit 4b2893b
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 126 deletions.
60 changes: 50 additions & 10 deletions src/style-spec/expression/definitions/in.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @flow

import {ValueType, BooleanType, toString} from '../types';
import assert from 'assert';

import {ValueType, BooleanType, toString, NumberType} from '../types';
import RuntimeError from '../runtime_error';
import {typeOf} from '../values';

Expand All @@ -10,6 +12,11 @@ import type EvaluationContext from '../evaluation_context';
import type {Type} from '../types';
import type {Value} from '../values';

const types = {
'in': BooleanType,
'index-of': NumberType
};

function isComparableType(type: Type) {
return type.kind === 'boolean' ||
type.kind === 'string' ||
Expand All @@ -21,7 +28,8 @@ function isComparableType(type: Type) {
function isComparableRuntimeValue(needle: boolean | string | number | null) {
return typeof needle === 'boolean' ||
typeof needle === 'string' ||
typeof needle === 'number';
typeof needle === 'number' ||
needle === null;
}

function isSearchableRuntimeValue(haystack: Array<Value> | string) {
Expand All @@ -33,18 +41,28 @@ class In implements Expression {
type: Type;
needle: Expression;
haystack: Expression;
fromIndex: ?Expression;

constructor(needle: Expression, haystack: Expression) {
this.type = BooleanType;
constructor(type: Type, needle: Expression, haystack: Expression, fromIndex?: Expression) {
this.type = type;
this.needle = needle;
this.haystack = haystack;
this.fromIndex = fromIndex;
}

static parse(args: $ReadOnlyArray<mixed>, context: ParsingContext) {
if (args.length !== 3) {
const name: string = (args[0]: any);
assert(types[name], name);

if (name === 'in' && args.length !== 3) {
return context.error(`Expected 2 arguments, but found ${args.length - 1} instead.`);
}

if (name === 'index-of' && (args.length <= 2 || args.length >= 5))
return context.error(`Expected 3 or 4 arguments, but found ${args.length - 1} instead.`);

const type = types[name];

const needle = context.parse(args[1], 1, ValueType);

const haystack = context.parse(args[2], 2, ValueType);
Expand All @@ -55,37 +73,59 @@ class In implements Expression {
return context.error(`Expected first argument to be of type boolean, string, number or null, but found ${toString(needle.type)} instead`);
}

return new In(needle, haystack);
if (args.length === 4) {
const fromIndex = context.parse(args[3], 3, NumberType);
if (!fromIndex) return null;
return new In(type, needle, haystack, fromIndex);
} else {
return new In(type, needle, haystack);
}
}

evaluate(ctx: EvaluationContext) {
const needle = (this.needle.evaluate(ctx): any);
const haystack = (this.haystack.evaluate(ctx): any);

if (needle == null || !haystack) return false;
if (!haystack && this.type.kind === 'boolean') return false;

if (!isComparableRuntimeValue(needle)) {
throw new RuntimeError(`Expected first argument to be of type boolean, string or number, but found ${toString(typeOf(needle))} instead.`);
throw new RuntimeError(`Expected first argument to be of type boolean, string, number or null, but found ${toString(typeOf(needle))} instead.`);
}

if (!isSearchableRuntimeValue(haystack)) {
throw new RuntimeError(`Expected second argument to be of type array or string, but found ${toString(typeOf(haystack))} instead.`);
}

return haystack.indexOf(needle) >= 0;
const fromIndex = this.fromIndex ? (this.fromIndex.evaluate(ctx): number) : 0;

if (this.type.kind === 'boolean') {
return haystack.indexOf(needle, fromIndex) >= 0;
} else {
return haystack.indexOf(needle, fromIndex);
}

}

eachChild(fn: (_: Expression) => void) {
fn(this.needle);
fn(this.haystack);
if (this.fromIndex) {
fn(this.fromIndex);
}
}

outputDefined() {
return true;
}

serialize() {
return ["in", this.needle.serialize(), this.haystack.serialize()];
const op = this.type.kind === 'boolean' ? 'in' : 'index-of';

if (this.fromIndex != null && this.fromIndex !== undefined) {
const fromIndex = this.fromIndex.serialize();
return [op, this.needle.serialize(), this.haystack.serialize(), fromIndex];
}
return [op, this.needle.serialize(), this.haystack.serialize()];
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/style-spec/expression/definitions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import Assertion from './assertion';
import Coercion from './coercion';
import At from './at';
import In from './in';
import IndexOf from './index_of';
import Match from './match';
import Case from './case';
import Slice from './slice';
Expand Down Expand Up @@ -66,7 +65,7 @@ const expressions: ExpressionRegistry = {
'format': FormatExpression,
'image': ImageExpression,
'in': In,
'index-of': IndexOf,
'index-of': In,
'interpolate': Interpolate,
'interpolate-hcl': Interpolate,
'interpolate-lab': Interpolate,
Expand Down
110 changes: 0 additions & 110 deletions src/style-spec/expression/definitions/index_of.js

This file was deleted.

2 changes: 2 additions & 0 deletions test/integration/expression-tests/in/basic-array/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
],
"inputs": [
[{}, {"properties": {"i": null, "arr": [9, 8, 7]}}],
[{}, {"properties": {"i": null, "arr": [9, null, 8, 7]}}],
[{}, {"properties": {"i": 1, "arr": [9, 8, 7]}}],
[{}, {"properties": {"i": 9, "arr": [9, 8, 7]}}],
[{}, {"properties": {"i": "foo", "arr": ["baz", "bar", "hello", "foo", "world"]}}],
Expand All @@ -20,6 +21,7 @@
},
"outputs": [
false,
true,
false,
true,
true,
Expand Down
4 changes: 2 additions & 2 deletions test/integration/expression-tests/in/invalid-needle/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"type": "boolean"
},
"outputs": [
{"error":"Expected first argument to be of type boolean, string or number, but found object instead."},
{"error":"Expected first argument to be of type boolean, string or number, but found object instead."}
{"error":"Expected first argument to be of type boolean, string, number or null, but found object instead."},
{"error":"Expected first argument to be of type boolean, string, number or null, but found object instead."}
],
"serialized": [
"boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
},
"outputs": [
{
"error": "Expected first argument to be of type boolean, string or number, but found object instead."
"error": "Expected first argument to be of type boolean, string, number or null, but found object instead."
},
{
"error": "Expected first argument to be of type boolean, string or number, but found object instead."
"error": "Expected first argument to be of type boolean, string, number or null, but found object instead."
}
],
"serialized": ["index-of", ["get", "needle"], ["get", "haystack"]]
Expand Down

0 comments on commit 4b2893b

Please sign in to comment.