Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project Arbex (Arbitrary Expressions) #4777

Merged
merged 4 commits into from
Aug 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
"no-eq-null": "off",
"no-lonely-if": "off",
"no-new": "off",
"no-unused-vars": ["error", {"argsIgnorePattern": "^_$"}],
"no-var": "error",
"no-warning-comments": "error",
"object-curly-spacing": "off",
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-const": ["error", {"destructuring": "all"}],
"prefer-template": "error",
"quotes": "off",
"space-before-function-paren": "off",
Expand Down
28 changes: 17 additions & 11 deletions build/generate-flow-typed-style-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,29 +130,35 @@ declare type TransitionSpecification = {
// Note: doesn't capture interpolatable vs. non-interpolatable types.

declare type CameraFunctionSpecification<T> =
| { type: 'exponential', stops: Array<[number, T]> }
| { type: 'interval', stops: Array<[number, T]> };
| {| type: 'exponential', stops: Array<[number, T]> |}
| {| type: 'interval', stops: Array<[number, T]> |};

declare type SourceFunctionSpecification<T> =
| { type: 'exponential', stops: Array<[number, T]>, property: string, default?: T }
| { type: 'interval', stops: Array<[number, T]>, property: string, default?: T }
| { type: 'categorical', stops: Array<[string | number | boolean, T]>, property: string, default?: T }
| { type: 'identity', property: string, default?: T };
| {| type: 'exponential', stops: Array<[number, T]>, property: string, default?: T |}
| {| type: 'interval', stops: Array<[number, T]>, property: string, default?: T |}
| {| type: 'categorical', stops: Array<[string | number | boolean, T]>, property: string, default?: T |}
| {| type: 'identity', property: string, default?: T |};

declare type CompositeFunctionSpecification<T> =
| { type: 'exponential', stops: Array<[{zoom: number, value: number}, T]>, property: string, default?: T }
| { type: 'interval', stops: Array<[{zoom: number, value: number}, T]>, property: string, default?: T }
| { type: 'categorical', stops: Array<[{zoom: number, value: string | number | boolean}, T]>, property: string, default?: T };
| {| type: 'exponential', stops: Array<[{zoom: number, value: number}, T]>, property: string, default?: T |}
| {| type: 'interval', stops: Array<[{zoom: number, value: number}, T]>, property: string, default?: T |}
| {| type: 'categorical', stops: Array<[{zoom: number, value: string | number | boolean}, T]>, property: string, default?: T |};

declare type ExpressionFunctionSpecification = {|
expression: mixed
|}

declare type PropertyValueSpecification<T> =
| T
| CameraFunctionSpecification<T>;
| CameraFunctionSpecification<T>
| ExpressionFunctionSpecification;

declare type DataDrivenPropertyValueSpecification<T> =
| T
| CameraFunctionSpecification<T>
| SourceFunctionSpecification<T>
| CompositeFunctionSpecification<T>;
| CompositeFunctionSpecification<T>
| ExpressionFunctionSpecification;

${flowObjectDeclaration('StyleSpecification', spec.$root)}

Expand Down
28 changes: 16 additions & 12 deletions debug/circles.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,25 @@
"source": "circles",
"paint": {
"circle-radius": {
property: "mapbox",
stops: [
[{ zoom: 0, value: 0 }, 2],
[{ zoom: 0, value: 100 }, 10],
[{ zoom: 6, value: 0 }, 20],
[{ zoom: 6, value: 100 }, 100]
"expression": [
"curve",
["linear"],
["zoom"],
0,
["curve", ["linear"], ["number", ["get", "scalerank"]], 0, 2, 10, 10],
6,
["curve", ["linear"], ["number", ["get", "scalerank"]], 0, 20, 10, 100]
]
},
"circle-color": {
property: "mapbox",
stops: [
[{ zoom: 0, value: 0 }, 'red'],
[{ zoom: 0, value: 100 }, 'violet'],
[{ zoom: 6, value: 0 }, 'blue'],
[{ zoom: 6, value: 100 }, 'green']
"expression": [
"curve",
["linear"],
["zoom"],
0,
["curve", ["linear"], ["number", ["get", "scalerank"]], 0, ["to-color", "red"], 6, ["to-color", "green"]],
6,
["curve", ["linear"], ["number", ["get", "scalerank"]], 0, ["to-color", "green"], 6, ["to-color", "blue"]],
]
},
"circle-pitch-scale": "map",
Expand Down
12 changes: 2 additions & 10 deletions docs/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
{
"parser": "espree",
"parserOptions": {
"ecmaVersion": 5
},
"env": {
"es6": false,
"browser": true
},
"globals": {
"Uint8Array": true
"rules": {
"flowtype/require-valid-file-annotation": [0]
}
}
8 changes: 7 additions & 1 deletion docs/_posts/examples/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
{
"parser": "espree",
"parserOptions": {
"ecmaVersion": 5
},
"plugins": ["html"],
"globals": {
"mapboxgl": true,
"MapboxGeocoder": true,
"MapboxDirections": true,
"turf": true,
"d3": true
"d3": true,
"Uint8Array": true
},
"rules": {
"flowtype/require-valid-file-annotation": [0],
Expand All @@ -17,6 +22,7 @@
"prefer-template": "off"
},
"env": {
"es6": false,
"browser": true
}
}
110 changes: 110 additions & 0 deletions docs/style-spec/_generate/expression-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict';
require('flow-remove-types/register');

const toString = require('../../../src/style-spec/function/types').toString;
const CompoundExpression = require('../../../src/style-spec/function/compound_expression').CompoundExpression;

// registers compound expressions
require('../../../src/style-spec/function/definitions');

const results = {
array: [{
type: 'Array',
parameters: ['Value'],
}, {
type: 'Array<type>',
parameters: [
{name: 'type', type: '"String" | "Number" | "Boolean"'},
'Value'
],
}, {
type: 'Array<type, N>',
parameters: [
{name: 'type', type: '"String" | "Number" | "Boolean"'},
{name: 'N', type: 'Number (literal)'},
'Value'
]
}],
at: [{
type: 'T',
parameters: ['Number', 'Array']
}],
case: [{
type: 'T',
parameters: [{ repeat: ['Boolean', 'T'] }, 'T']
}],
coalesce: [{
type: 'T',
parameters: [{repeat: 'T'}]
}],
contains: [{
type: 'Boolean',
parameters: ['T', 'Array<T> | Array<T, N>']
}],
curve: [{
type: 'T',
parameters: [
{name: 'input', type: 'Number'},
'["step"]',
'T',
{repeat: ['Number', 'T']}
]
}, {
type: 'T: Number, ',
parameters: [
{name: 'input', type: 'Number'},
{name: 'interpolation', type: '["step"] | ["linear"] | ["exponential", base] | ["cubic-bezier", x1, y1, x2, y2 ]'},
{repeat: ['Number', 'T']}
]
}],
let: [{
type: 'T',
parameters: [{ repeat: ['String (alphanumeric literal)', 'any']}, 'T']
}],
literal: [{
type: 'Array<T, N>',
parameters: ['[...] (JSON array literal)']
}, {
type: 'Object',
parameters: ['{...} (JSON object literal)']
}],
match: [{
type: 'U',
parameters: [
{name: 'input', type: 'T: Number (integer literal) | String (literal)'},
{repeat: ['T | [T, T, ...]', 'U']},
'U'
]
}],
var: [{
type: 'the type of the bound expression',
parameters: ['previously bound variable name']
}]
};

for (const name in CompoundExpression.definitions) {
const definition = CompoundExpression.definitions[name];
if (Array.isArray(definition)) {
results[name] = [{
type: toString(definition[0]),
parameters: processParameters(definition[1])
}];
} else {
results[name] = definition.overloads.map((o) => {
return {
type: toString(definition.type),
parameters: processParameters(o[0])
};
});
}
}

function processParameters(params) {
if (Array.isArray(params)) {
return params.map(toString);
} else {
return [{repeat: [toString(params.type)]}];
}
}

module.exports = results;
12 changes: 12 additions & 0 deletions docs/style-spec/_generate/expression.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class='col12 clearfix pad0y pad2x space-top0'>
<span class='space-right'>
<%= md("<a class='code' id='types-expression-" + name + "' href='#types-expression-" + name + "'>" + name + "</a> " + (expressionDocs[name] ? ('<br>' + expressionDocs[name].doc) : '').trim()) %>
</span>
<% for (const overload of expressionTypes[name]) { %>
<div>
{% highlight javascript %}
<%=renderSignature(name, overload) %>
{% endhighlight %}
</div>
<% } %>
</div>
44 changes: 43 additions & 1 deletion docs/style-spec/_generate/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ var _ = require('lodash');
var remark = require('remark');
var html = require('remark-html');

var expressionTypes = require('./expression-types');


function tmpl(x, options) {
return _.template(fs.readFileSync(path.join(__dirname, x), 'utf-8'), options);
return _.template(fs.readFileSync(path.join(__dirname, x), 'utf-8'), options);
}

var index = tmpl('index.html', {
Expand All @@ -23,8 +26,47 @@ var index = tmpl('index.html', {
return remark().use(html).process(markdown);
}
}
}),
expressions: Object.keys(expressionTypes).sort((a, b) => a.localeCompare(b)),
renderExpression: tmpl('expression.html', {
imports: {
_: _,
expressionDocs: ref['expression_name'].values,
expressionTypes: expressionTypes,
renderSignature: renderSignature,
md: function(markdown) {
return remark().use(html).process(markdown)
}
}
})
}
});

function renderSignature (name, overload) {
name = JSON.stringify(name);
const maxLength = 80 - name.length - overload.type.length;
const params = renderParams(overload.parameters, maxLength);
return `[${name}${params}]: ${overload.type}`;
}

function renderParams (params, maxLength) {
const result = [''];
for (const t of params) {
if (typeof t === 'string') {
result.push(t);
} else if (t.name) {
result.push(`${t.name}: ${t.type}`);
} else if (t.repeat) {
const repeated = renderParams(t.repeat, Infinity);
result.push(`${repeated.slice(2)}${repeated}, ...`);
}
}

// length of result = each (', ' + item)
const length = result.reduce((l, s) => l + s.length + 2, 0);
return (!maxLength || length <= maxLength) ?
result.join(', ') :
`${result.join(',\n ')}\n`;
}

fs.writeFileSync(path.join(__dirname, '../index.html'), index({ ref: ref }));
Loading