Skip to content

Commit

Permalink
Fold compound expressions with literal arguments into literals
Browse files Browse the repository at this point in the history
  • Loading branch information
Anand Thakker committed Sep 13, 2017
1 parent 953015b commit aced1fb
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 10 deletions.
32 changes: 29 additions & 3 deletions src/style-spec/function/compound_expression.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// @flow

const { toString } = require('./types');
const { ParsingContext } = require('./expression');
const { ParsingContext, CompilationContext } = require('./expression');
const parseExpression = require('./parse_expression');
const checkSubtype = require('./check_subtype');
const assert = require('assert');
const Literal = require('./definitions/literal');
const evaluationContext = require('./evaluation_context');

import type { Expression, CompilationContext } from './expression';
import type { Expression } from './expression';
import type { Type } from './types';

type Varargs = {| type: Type |};
Expand All @@ -32,6 +34,10 @@ class CompoundExpression implements Expression {
this.args = args;
}

static create(ctx: ParsingContext, name: string, type: Type, compileFromArgs: Compile, args: Array<Expression>) {
return foldConstantExpression(ctx, new CompoundExpression(ctx.key, name, type, compileFromArgs, args));
}

compile(ctx: CompilationContext) {
return this.compileFromArgs(ctx, this.args);
}
Expand Down Expand Up @@ -102,7 +108,7 @@ class CompoundExpression implements Expression {
}

if (signatureContext.errors.length === 0) {
return new CompoundExpression(context.key, op, type, compileFromArgs, parsedArgs);
return CompoundExpression.create(context, op, type, compileFromArgs, parsedArgs);
}
}

Expand Down Expand Up @@ -147,6 +153,26 @@ function stringifySignature(signature: Signature): string {
}
}

const lookupExpressions = [ 'get', 'has', 'properties', 'id', 'geometry-type', 'zoom' ];
function foldConstantExpression(ctx: ParsingContext, expression: CompoundExpression) {
const isConstant = lookupExpressions.indexOf(expression.name) < 0 &&
!expression.args.some(a => !(a instanceof Literal));

if (isConstant) {
const cc = new CompilationContext();
const ec = evaluationContext();
const compiled = cc.compileToFunction(expression, ec);
try {
const value = compiled({}, {});
return new Literal(expression.key, expression.type, value);
} catch (e) {
return ctx.error(e.message);
}
}

return expression;
}

module.exports = {
CompoundExpression,
varargs
Expand Down
4 changes: 2 additions & 2 deletions src/style-spec/function/definitions/curve.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ class Curve implements Expression {
const label = rest[i];
const value = rest[i + 1];

const labelKey = isStep ? i + 4 : i + 3;
const valueKey = isStep ? i + 5 : i + 4;
const labelKey = isStep ? i + 2 : i + 3;
const valueKey = isStep ? i + 3 : i + 4;

if (typeof label !== 'number') {
return context.error('Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.', labelKey);
Expand Down
19 changes: 15 additions & 4 deletions src/style-spec/function/definitions/literal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { Color, isValue, typeOf } = require('../values');

import type { Type } from '../types';
import type { Value } from '../values';
import type { Expression, ParsingContext } from '../expression';
import type { Expression, ParsingContext, CompilationContext } from '../expression';

class Literal implements Expression {
key: string;
Expand Down Expand Up @@ -42,9 +42,20 @@ class Literal implements Expression {
return new Literal(context.key, type, value);
}

compile() {
const value = JSON.stringify(this.value);
return typeof this.value === 'object' ? `(${value})` : value;
compile(ctx: CompilationContext) {
let value;
if (this.type.kind === 'Color') {
value = `(new $this.Color(${(this.value: any).join(', ')}))`;
} else {
value = JSON.stringify(this.value);
}

if (typeof this.value === 'object' && this.value !== null) {
const v = ctx.addVariable(value);
return v;
} else {
return value;
}
}

serialize() {
Expand Down
1 change: 1 addition & 0 deletions src/style-spec/function/evaluation_context.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function ensure(condition: any, message: string) {

module.exports = () => ({
types: types,
Color: Color,

ensure: ensure,
error: (msg: string) => ensure(false, msg),
Expand Down
4 changes: 3 additions & 1 deletion src/style-spec/function/parse_expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ function parseExpression(expr: mixed, context: ParsingContext): ?Expression {
parsed = wrapForType(expected, parsed, context);
}

if (!parsed) return null;

if (checkSubtype(expected, parsed.type, context)) {
return null;
}
Expand All @@ -70,7 +72,7 @@ function wrapForType(expected: Type, expression: Expression, context: ParsingCon
// workaround for circular dependency
const CompoundExpr: Class<CompoundExpression> = (context.definitions['to-color']: any);
const definition = CompoundExpr.definitions['to-color'];
return new CompoundExpr(expression.key, 'to-color', expected, definition[2], [expression]);
return CompoundExpr.create(context, 'to-color', expected, definition[2], [expression]);
} else if (
expected.kind === 'Number' ||
expected.kind === 'String' ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"expectExpressionType": {"kind": "Color"},
"expression": [
"curve",
["step"],
["get", "x"],
"black",
0,
"invalid",
10,
"blue"
],
"inputs": [
[{}, {"properties": {"x": -1}}],
[{}, {"properties": {"x": 0}}],
[{}, {"properties": {"x": 5}}],
[{}, {"properties": {"x": 10}}],
[{}, {"properties": {"x": 11}}]
],
"expected": {
"compiled": {
"result": "error",
"errors": [
{"key": "[5]", "error": "Could not parse color from value 'invalid'"}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"expectExpressionType": {"kind": "Color"},
"expression": [
"curve",
["step"],
["get", "x"],
"black",
0,
"red",
10,
"blue"
],
"inputs": [
[{}, {"properties": {"x": -1}}],
[{}, {"properties": {"x": 0}}],
[{}, {"properties": {"x": 5}}],
[{}, {"properties": {"x": 10}}],
[{}, {"properties": {"x": 11}}]
],
"expected": {
"compiled": {
"result": "success",
"isFeatureConstant": false,
"isZoomConstant": true,
"type": "Color"
},
"outputs": [
[0, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[0, 0, 1, 1],
[0, 0, 1, 1]
]
}
}

0 comments on commit aced1fb

Please sign in to comment.