From 593092fedd1e4a921288c9be10b017a2b0dc0ad9 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Fri, 22 Jun 2012 12:30:17 -0400 Subject: [PATCH 01/16] Support backwards-compatibility for `functions` type specified as strings, like in `point-transform` --- lib/carto/tree/reference.js | 77 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/lib/carto/tree/reference.js b/lib/carto/tree/reference.js index 49914f1..a5ded89 100644 --- a/lib/carto/tree/reference.js +++ b/lib/carto/tree/reference.js @@ -1,3 +1,9 @@ +/* + * Carto pulls in a reference from the `mapnik-reference` + * module. This file builds indexes from that file for its various + * options, and provides validation methods for property: value + * combinations. + */ (function(tree) { var _ = require('underscore'); @@ -101,31 +107,32 @@ tree.Reference.isFont = function(selector) { tree.Reference.validValue = function(env, selector, value) { var i, j; - if (value[0]) { - return tree.Reference.selector(selector).type == value[0].is; - } else { - // TODO: handle in reusable way - if (!tree.Reference.selector(selector)) { - return false; - } else if (value.value[0].is == 'keyword') { - return tree.Reference - .selector(selector).type - .indexOf(value.value[0].value) !== -1; - } else if (value.value[0].is == 'undefined') { - // caught earlier in the chain - ignore here so that - // error is not overridden - return true; - } else if (tree.Reference.selector(selector).type == 'numbers') { - for (i in value.value) { - if (value.value[i].is !== 'float') { - return false; - } + // TODO: handle in reusable way + if (!tree.Reference.selector(selector)) { + return false; + } else if (value.value[0].is == 'keyword') { + return tree.Reference + .selector(selector).type + .indexOf(value.value[0].value) !== -1; + } else if (value.value[0].is == 'undefined') { + // caught earlier in the chain - ignore here so that + // error is not overridden + return true; + } else if (tree.Reference.selector(selector).type == 'numbers') { + for (i in value.value) { + if (value.value[i].is !== 'float') { + return false; } + } + return true; + } else if (tree.Reference.selector(selector).type == 'functions') { + // For backwards compatibility, you can specify a string for `functions`-compatible + // values, though they will not be validated. + if (value.value[0].is === 'string') { return true; - } else if (tree.Reference.selector(selector).type == 'functions') { + } else { for (i in value.value) { for (j in value.value[i].value) { - console.log(value.value[i]); if (value.value[i].value[j].is !== 'call') { return false; } @@ -140,22 +147,22 @@ tree.Reference.validValue = function(env, selector, value) { } } return true; - } else { - if (tree.Reference.selector(selector).validate) { - var valid = false; - for (i = 0; i < value.value.length; i++) { - if (tree.Reference.selector(selector).type == value.value[i].is && - tree.Reference - ._validateValue - [tree.Reference.selector(selector).validate] - (env, value.value[i].value)) { - return true; - } + } + } else { + if (tree.Reference.selector(selector).validate) { + var valid = false; + for (i = 0; i < value.value.length; i++) { + if (tree.Reference.selector(selector).type == value.value[i].is && + tree.Reference + ._validateValue + [tree.Reference.selector(selector).validate] + (env, value.value[i].value)) { + return true; } - return valid; - } else { - return tree.Reference.selector(selector).type == value.value[0].is; } + return valid; + } else { + return tree.Reference.selector(selector).type == value.value[0].is; } } }; From e27ce12ea074e050347b639e533a581068a007dd Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Fri, 22 Jun 2012 12:32:36 -0400 Subject: [PATCH 02/16] Support variables and expressions as the arguments to functions. --- lib/carto/tree/call.js | 8 ++++++-- test/rendering/transforms.mss | 3 ++- test/rendering/transforms.result | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/carto/tree/call.js b/lib/carto/tree/call.js index cb17b81..191b85c 100644 --- a/lib/carto/tree/call.js +++ b/lib/carto/tree/call.js @@ -62,13 +62,17 @@ tree.Call.prototype = { toString: function(env, format) { if (format === 'image-filter') { if (this.args.length) { - return this.name + ':' + this.args.join(','); + return this.name + ':' + this.args.map(function(a) { + return a.eval(env); + }).join(','); } else { return this.name; } } else { if (this.args.length) { - return this.name + '(' + this.args.join(',') + ')'; + return this.name + '(' + this.args.map(function(a) { + return a.eval(env); + }).join(',') + ')'; } else { return this.name; } diff --git a/test/rendering/transforms.mss b/test/rendering/transforms.mss index fe08902..628b984 100644 --- a/test/rendering/transforms.mss +++ b/test/rendering/transforms.mss @@ -1,4 +1,5 @@ +@trans: 2; #world { point-file: url(foo.png); - point-transform: translate(2, 2); + point-transform: translate(@trans * 2, @trans); } diff --git a/test/rendering/transforms.result b/test/rendering/transforms.result index 147979f..bda0bab 100644 --- a/test/rendering/transforms.result +++ b/test/rendering/transforms.result @@ -5,7 +5,7 @@ Date: Fri, 22 Jun 2012 12:38:20 -0400 Subject: [PATCH 03/16] Add backwards compatibility for transforms tests --- test/rendering/transforms_backwards.mml | 14 ++++++++++++++ test/rendering/transforms_backwards.mss | 4 ++++ test/rendering/transforms_backwards.result | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 test/rendering/transforms_backwards.mml create mode 100644 test/rendering/transforms_backwards.mss create mode 100644 test/rendering/transforms_backwards.result diff --git a/test/rendering/transforms_backwards.mml b/test/rendering/transforms_backwards.mml new file mode 100644 index 0000000..b901d36 --- /dev/null +++ b/test/rendering/transforms_backwards.mml @@ -0,0 +1,14 @@ +{ + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Stylesheet": [ + "transforms_backwards.mss" + ], + "Layer": [{ + "name": "world", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }] +} diff --git a/test/rendering/transforms_backwards.mss b/test/rendering/transforms_backwards.mss new file mode 100644 index 0000000..f7d75e1 --- /dev/null +++ b/test/rendering/transforms_backwards.mss @@ -0,0 +1,4 @@ +#world { + point-file: url(foo.png); + point-transform: "translate(2, 2)"; +} diff --git a/test/rendering/transforms_backwards.result b/test/rendering/transforms_backwards.result new file mode 100644 index 0000000..9836692 --- /dev/null +++ b/test/rendering/transforms_backwards.result @@ -0,0 +1,20 @@ + + + + + + + + world + + + + + + + From 265554813b2ccdce9c4a609f7262305d7b33745f Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Fri, 22 Jun 2012 18:06:15 -0400 Subject: [PATCH 04/16] Update results for temporary image-transform renaming, update reference to mapnik-reference to point to the transforms branch, prep a changelog message --- CHANGELOG.md | 4 ++ lib/carto/tree/call.js | 44 ++++++++++++++++------ lib/carto/tree/reference.js | 18 +++++++++ package.json | 2 +- test/rendering/transforms.result | 2 +- test/rendering/transforms_backwards.result | 2 +- 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3854c57..91220d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Changelog +### 0.8.0 + +* Supports function syntax for transforms, optionally with variables and arguments. + ### 0.7.0 * Support an `opacity` property on any style that is a style-level property diff --git a/lib/carto/tree/call.js b/lib/carto/tree/call.js index 191b85c..d1a2fb6 100644 --- a/lib/carto/tree/call.js +++ b/lib/carto/tree/call.js @@ -1,8 +1,5 @@ (function(tree) { -// -// A function call node. -// tree.Call = function Call(name, args, index) { this.is = 'call'; @@ -53,26 +50,49 @@ tree.Call.prototype = { }; } } else { - return this; - // return new tree.Anonymous(this.name + - // '(' + args.map(function(a) { return a.toString(); }).join(', ') + ')'); + var fn = tree.Reference.mapnikFunction(this.name); + if (!fn) { + env.error({ + message: 'unknown function ' + this.name, + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + if (fn[1] !== args.length) { + env.error({ + message: 'function ' + this.name + ' takes ' + + fn[1] + ' arguments and was given ' + args.length, + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } else { + // Save the evaluated versions of arguments + this.args = args; + return this; + } } }, toString: function(env, format) { if (format === 'image-filter') { if (this.args.length) { - return this.name + ':' + this.args.map(function(a) { - return a.eval(env); - }).join(','); + return this.name + ':' + this.args.join(','); } else { return this.name; } } else { if (this.args.length) { - return this.name + '(' + this.args.map(function(a) { - return a.eval(env); - }).join(',') + ')'; + return this.name + '(' + this.args.join(',') + ')'; } else { return this.name; } diff --git a/lib/carto/tree/reference.js b/lib/carto/tree/reference.js index a5ded89..7a81cdc 100644 --- a/lib/carto/tree/reference.js +++ b/lib/carto/tree/reference.js @@ -65,6 +65,24 @@ tree.Reference.symbolizer = function(selector) { } }; +/* + * For transform properties and image-filters, + * mapnik has its own functions. + */ +tree.Reference.mapnikFunction = function(name) { + var functions = []; + for (var i in tree.Reference.data.symbolizers) { + for (var j in tree.Reference.data.symbolizers[i]) { + if (tree.Reference.data.symbolizers[i][j].type === 'functions') { + functions = functions.concat(tree.Reference.data.symbolizers[i][j].functions); + } + } + } + return _.find(functions, function(f) { + return f[0] === name; + }); +}; + tree.Reference.requiredPropertyList = function(symbolizer_name) { if (this.required_prop_list_cache[symbolizer_name]) { return this.required_prop_list_cache[symbolizer_name]; diff --git a/package.json b/package.json index f07837e..ae1c170 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "underscore": "~1.3.3", - "mapnik-reference": "~2.1.0", + "mapnik-reference": "https://github.com/mapnik/mapnik-reference/zipball/transform-functions", "xml2js": "~0.1.13" }, "devDependencies": { diff --git a/test/rendering/transforms.result b/test/rendering/transforms.result index bda0bab..80c5961 100644 --- a/test/rendering/transforms.result +++ b/test/rendering/transforms.result @@ -5,7 +5,7 @@ - + Date: Fri, 22 Jun 2012 18:08:35 -0400 Subject: [PATCH 05/16] Point to tarball, not zip file --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae1c170..95d095f 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "underscore": "~1.3.3", - "mapnik-reference": "https://github.com/mapnik/mapnik-reference/zipball/transform-functions", + "mapnik-reference": "https://github.com/mapnik/mapnik-reference/tarball/master", "xml2js": "~0.1.13" }, "devDependencies": { From 8a972580255e98db564abde7684c3002ebb8b8bf Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Fri, 22 Jun 2012 18:10:16 -0400 Subject: [PATCH 06/16] Point to transform-functions branch --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 95d095f..35ac355 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "underscore": "~1.3.3", - "mapnik-reference": "https://github.com/mapnik/mapnik-reference/tarball/master", + "mapnik-reference": "https://github.com/mapnik/mapnik-reference/tarball/transform-functions", "xml2js": "~0.1.13" }, "devDependencies": { From ea89af6ae2e3cc049286130140ff932597a87171 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Fri, 22 Jun 2012 18:35:00 -0400 Subject: [PATCH 07/16] Add field type, parsing, and failing test. --- lib/carto/index.js | 2 +- lib/carto/parser.js | 26 +++++++++++++++++++++++--- lib/carto/tree/field.js | 16 ++++++++++++++++ test/rendering/field.mml | 23 +++++++++++++++++++++++ test/rendering/field.mss | 6 ++++++ 5 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 lib/carto/tree/field.js create mode 100644 test/rendering/field.mml create mode 100644 test/rendering/field.mss diff --git a/lib/carto/index.js b/lib/carto/index.js index 5ccd5d6..34b3c89 100644 --- a/lib/carto/index.js +++ b/lib/carto/index.js @@ -51,7 +51,7 @@ var carto = { }; [ 'call', 'color', 'comment', 'definition', 'dimension', - 'directive', 'element', 'expression', 'filterset', 'filter', + 'directive', 'element', 'expression', 'filterset', 'filter', 'field', 'keyword', 'layer', 'operation', 'quoted', 'imagefilter', 'reference', 'rule', 'ruleset', 'selector', 'style', 'url', 'value', 'variable', 'zoom', 'invalid', 'fontset' diff --git a/lib/carto/parser.js b/lib/carto/parser.js index a8312bc..99eb50d 100644 --- a/lib/carto/parser.js +++ b/lib/carto/parser.js @@ -423,6 +423,20 @@ carto.Parser = function Parser(env) { } }, + // A reference to a Mapnik field, like + // + // [NAME] + // + // Behind the scenes, this has the same representation, but Carto + // needs to be careful to warn when unsupported operations are used. + field: function() { + if (! $('[')) return; + var field_name = $(/(^[a-zA-Z0-9\-_]+)/); + if (! $(']')) return; + if (field_name) return new tree.Field(field_name[1]); + }, + + // This is a comparison operator comparison: function() { var str = $(/^=~|=|!=|<=|>=|<|>/); if (str) { @@ -553,7 +567,9 @@ carto.Parser = function Parser(env) { variable: function() { var name; - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } + if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { + return name[1]; + } }, // @@ -561,8 +577,12 @@ carto.Parser = function Parser(env) { // and can be found inside a rule's value. // entity: function() { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword); + return $(this.entities.literal) || + $(this.entities.field) || + $(this.entities.variable) || + $(this.entities.url) || + $(this.entities.call) || + $(this.entities.keyword); }, // diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js new file mode 100644 index 0000000..600e264 --- /dev/null +++ b/lib/carto/tree/field.js @@ -0,0 +1,16 @@ +(function(tree) { + +tree.Field = function Field(content) { + this.value = content || ''; + this.is = 'field'; +}; +tree.Field.prototype = { + toString: function(quotes) { + return '[' + this.value + ']'; + }, + 'eval': function() { + return this; + } +}; + +})(require('../tree')); diff --git a/test/rendering/field.mml b/test/rendering/field.mml new file mode 100644 index 0000000..94629dc --- /dev/null +++ b/test/rendering/field.mml @@ -0,0 +1,23 @@ +{ + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Stylesheet": [ + "field.mss" + ], + "Layer": [{ + "name": "world", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }, + { + "class": "new", + "name": "countries", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }] +} diff --git a/test/rendering/field.mss b/test/rendering/field.mss new file mode 100644 index 0000000..4eb934b --- /dev/null +++ b/test/rendering/field.mss @@ -0,0 +1,6 @@ +#world { + text-name: [NAME]; + text-size: 11; + text-face-name: "Georgia Regular", "Arial Italic"; +} + From 28ab872e724790613bbe99173bf17c0fe0d4f0a1 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 12:14:13 -0400 Subject: [PATCH 08/16] Support operations between strings and fields, support concatenating strings. --- lib/carto/parser.js | 8 +++++--- lib/carto/tree/field.js | 3 ++- lib/carto/tree/operation.js | 22 ++++++++++++---------- lib/carto/tree/quoted.js | 6 ++++++ lib/carto/tree/reference.js | 2 ++ test/rendering/field.mss | 2 +- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/lib/carto/parser.js b/lib/carto/parser.js index 99eb50d..046781a 100644 --- a/lib/carto/parser.js +++ b/lib/carto/parser.js @@ -763,6 +763,7 @@ carto.Parser = function Parser(env) { return new tree.Value(expressions); } }, + // A sub-expression, contained by parenthensis sub: function() { var e; @@ -770,12 +771,15 @@ carto.Parser = function Parser(env) { return e; } }, + // This is a misnomer because it actually handles multiplication + // and division. multiplication: function() { var m, a, op, operation; if (m = $(this.operand)) { while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { operation = new tree.Operation(op, [operation || m, a], memo); } + console.log(m); return operation || m; } }, @@ -793,9 +797,7 @@ carto.Parser = function Parser(env) { // An operand is anything that can be part of an operation, // such as a Color, or a Variable operand: function() { - return $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); + return $(this.sub) || $(this.entity); }, // Expressions either represent mathematical operations, diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js index 600e264..f6ebe8d 100644 --- a/lib/carto/tree/field.js +++ b/lib/carto/tree/field.js @@ -4,8 +4,9 @@ tree.Field = function Field(content) { this.value = content || ''; this.is = 'field'; }; + tree.Field.prototype = { - toString: function(quotes) { + toString: function() { return '[' + this.value + ']'; }, 'eval': function() { diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index 8fd0e87..a536fad 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -31,16 +31,18 @@ tree.Operation.prototype.eval = function(env) { } if (a instanceof tree.Quoted || b instanceof tree.Quoted) { - env.error({ - message: 'One cannot add, subtract, divide, or multiply strings.', - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; + if (this.op === '*' || this.op === '/') { + env.error({ + message: 'One cannot add, subtract, divide, or multiply strings.', + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } } return a.operate(this.op, b); diff --git a/lib/carto/tree/quoted.js b/lib/carto/tree/quoted.js index 9c30a3d..885e4c5 100644 --- a/lib/carto/tree/quoted.js +++ b/lib/carto/tree/quoted.js @@ -10,8 +10,14 @@ tree.Quoted.prototype = { var xmlvalue = this.value.replace(/\'/g, '''); return (quotes === true) ? "'" + xmlvalue + "'" : this.value; }, + 'eval': function() { return this; + }, + + operate: function(op, other) { + return new tree.Quoted(this.quote, + tree.operate(op, this.toString(), other.toString())); } }; diff --git a/lib/carto/tree/reference.js b/lib/carto/tree/reference.js index 7a81cdc..e46622b 100644 --- a/lib/carto/tree/reference.js +++ b/lib/carto/tree/reference.js @@ -166,6 +166,8 @@ tree.Reference.validValue = function(env, selector, value) { } return true; } + } else if (tree.Reference.selector(selector).type == 'expression') { + return true; } else { if (tree.Reference.selector(selector).validate) { var valid = false; diff --git a/test/rendering/field.mss b/test/rendering/field.mss index 4eb934b..f5bb71f 100644 --- a/test/rendering/field.mss +++ b/test/rendering/field.mss @@ -1,5 +1,5 @@ #world { - text-name: [NAME]; + text-name: "hello " + [NAME]; text-size: 11; text-face-name: "Georgia Regular", "Arial Italic"; } From bdfcd7aa6d2f29d81b8df1cb5fca7c3e386102b7 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 12:37:05 -0400 Subject: [PATCH 09/16] Note some new features in the changelog, add modulus operation --- CHANGELOG.md | 5 +++++ lib/carto/parser.js | 5 ++--- lib/carto/tree/operation.js | 1 + test/rendering/field.result | 23 +++++++++++++++++++++++ test/rendering/modulus.mml | 23 +++++++++++++++++++++++ test/rendering/modulus.mss | 6 ++++++ test/rendering/modulus.result | 23 +++++++++++++++++++++++ 7 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 test/rendering/field.result create mode 100644 test/rendering/modulus.mml create mode 100644 test/rendering/modulus.mss create mode 100644 test/rendering/modulus.result diff --git a/CHANGELOG.md b/CHANGELOG.md index 91220d6..6eb0416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Changelog +## 0.9.0 + +* Adds the modulus operator `%` as an option +* Adds a new field-type like `[FIELD]` instead of "[FIELD" + ### 0.8.0 * Supports function syntax for transforms, optionally with variables and arguments. diff --git a/lib/carto/parser.js b/lib/carto/parser.js index 046781a..a17dd38 100644 --- a/lib/carto/parser.js +++ b/lib/carto/parser.js @@ -463,7 +463,7 @@ carto.Parser = function Parser(env) { call: function() { var name, args; - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; + if (! (name = /^([\w\-]+|%)\(/.exec(chunks[j]))) return; name = name[1].toLowerCase(); @@ -776,10 +776,9 @@ carto.Parser = function Parser(env) { multiplication: function() { var m, a, op, operation; if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { + while ((op = ($('/') || $('*') || $('%'))) && (a = $(this.operand))) { operation = new tree.Operation(op, [operation || m, a], memo); } - console.log(m); return operation || m; } }, diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index a536fad..1a26db4 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -53,6 +53,7 @@ tree.operate = function(op, a, b) { case '+': return a + b; case '-': return a - b; case '*': return a * b; + case '%': return a % b; case '/': return a / b; } }; diff --git a/test/rendering/field.result b/test/rendering/field.result new file mode 100644 index 0000000..f7ea5af --- /dev/null +++ b/test/rendering/field.result @@ -0,0 +1,23 @@ + + + + + + + + + + + world + + + + + + + diff --git a/test/rendering/modulus.mml b/test/rendering/modulus.mml new file mode 100644 index 0000000..e46eb87 --- /dev/null +++ b/test/rendering/modulus.mml @@ -0,0 +1,23 @@ +{ + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Stylesheet": [ + "modulus.mss" + ], + "Layer": [{ + "name": "world", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }, + { + "class": "new", + "name": "countries", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }] +} diff --git a/test/rendering/modulus.mss b/test/rendering/modulus.mss new file mode 100644 index 0000000..e9d797d --- /dev/null +++ b/test/rendering/modulus.mss @@ -0,0 +1,6 @@ +#world { + text-name: "hello "; + text-size: 11 % 2; + text-face-name: "Georgia Regular", "Arial Italic"; +} + diff --git a/test/rendering/modulus.result b/test/rendering/modulus.result new file mode 100644 index 0000000..b0f7020 --- /dev/null +++ b/test/rendering/modulus.result @@ -0,0 +1,23 @@ + + + + + + + + + + + world + + + + + + + From d7e6cdf082012c418cfdc70383da243ad50edec9 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 12:57:10 -0400 Subject: [PATCH 10/16] Improve operating with mapnik field types --- lib/carto/functions.js | 7 +++++++ lib/carto/tree/dimension.js | 8 ++++++-- lib/carto/tree/field.js | 3 +++ lib/carto/tree/quoted.js | 9 +++++++-- test/rendering/building_height.mml | 23 +++++++++++++++++++++++ test/rendering/building_height.mss | 4 ++++ 6 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 test/rendering/building_height.mml create mode 100644 test/rendering/building_height.mss diff --git a/lib/carto/functions.js b/lib/carto/functions.js index 47129ef..f569589 100644 --- a/lib/carto/functions.js +++ b/lib/carto/functions.js @@ -94,6 +94,13 @@ tree.functions = { return hsla(hsl); }, + replace: function (entity, a, b) { + if (entity.is === 'field') { + return entity.toString + '.replace(' + a.toString() + ', ' + b.toString() + ')'; + } else { + return entity.replace(a, b); + } + }, // // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein // http://sass-lang.com diff --git a/lib/carto/tree/dimension.js b/lib/carto/tree/dimension.js index dfa7de2..471e439 100644 --- a/lib/carto/tree/dimension.js +++ b/lib/carto/tree/dimension.js @@ -35,8 +35,12 @@ tree.Dimension.prototype = { // conversions such that `100cm + 10mm` would yield // `101cm`. operate: function(op, other) { - return new tree.Dimension(tree.operate(op, this.value, other.value), - this.unit || other.unit); + if (other.is === 'field') { + return new tree.Quoted('', this.value + op + other.toString()); + } else { + return new tree.Dimension(tree.operate(op, this.value, other.value), + this.unit || other.unit); + } } }; diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js index f6ebe8d..e18eb39 100644 --- a/lib/carto/tree/field.js +++ b/lib/carto/tree/field.js @@ -11,6 +11,9 @@ tree.Field.prototype = { }, 'eval': function() { return this; + }, + operate: function(op, other) { + return new tree.Quoted('', this.toString() + op + other.toString()); } }; diff --git a/lib/carto/tree/quoted.js b/lib/carto/tree/quoted.js index 885e4c5..67d0add 100644 --- a/lib/carto/tree/quoted.js +++ b/lib/carto/tree/quoted.js @@ -16,8 +16,13 @@ tree.Quoted.prototype = { }, operate: function(op, other) { - return new tree.Quoted(this.quote, - tree.operate(op, this.toString(), other.toString())); + if (other.is !== 'string') { + return new tree.Quoted(this.quote, + this.toString() + op + other.toString()); + } else { + return new tree.Quoted(this.quote, + tree.operate(op, this.toString(), other.toString())); + } } }; diff --git a/test/rendering/building_height.mml b/test/rendering/building_height.mml new file mode 100644 index 0000000..79ba517 --- /dev/null +++ b/test/rendering/building_height.mml @@ -0,0 +1,23 @@ +{ + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Stylesheet": [ + "building_height.mss" + ], + "Layer": [{ + "name": "world", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }, + { + "class": "new", + "name": "countries", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }] +} diff --git a/test/rendering/building_height.mss b/test/rendering/building_height.mss new file mode 100644 index 0000000..6122c84 --- /dev/null +++ b/test/rendering/building_height.mss @@ -0,0 +1,4 @@ +@n: 4; +#world { + building-height: 2 * 3 * [HEIGHT] + 2 + [NOTHEIGHT] + (@n * 2); +} From d244fd0da8270cbd3e3ed23b64f9f7dd432cb669 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 14:38:51 -0400 Subject: [PATCH 11/16] Sys is now util, switch to util --- bin/carto | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/carto b/bin/carto index 430c3d7..2c5d262 100755 --- a/bin/carto +++ b/bin/carto @@ -2,7 +2,7 @@ var path = require('path'), fs = require('fs'), - sys = require('sys'), + util = require('util'), carto = require('carto'); var args = process.argv.slice(1); @@ -17,7 +17,7 @@ args = args.filter(function (arg) { switch (arg) { case 'v': case 'version': - sys.puts("carto " + carto.version.join('.') + " (Carto map stylesheet compiler)"); + util.puts("carto " + carto.version.join('.') + " (Carto map stylesheet compiler)"); process.exit(0); break; case 'b': @@ -26,10 +26,10 @@ args = args.filter(function (arg) { break; default: - sys.puts("Usage: carto "); - sys.puts("Options:"); - sys.puts(" -v --version Parse JSON map manifest"); - sys.puts(" -b --benchmark Outputs total compile time"); + util.puts("Usage: carto "); + util.puts("Options:"); + util.puts(" -v --version Parse JSON map manifest"); + util.puts(" -b --benchmark Outputs total compile time"); process.exit(0); break; } @@ -41,7 +41,7 @@ if (input && input[0] != '/') { } if (!input) { - sys.puts("carto: no input files"); + util.puts("carto: no input files"); process.exit(1); } @@ -52,14 +52,14 @@ if (options.benchmark) { try { var data = fs.readFileSync(input, 'utf-8'); } catch(err) { - sys.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); + util.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); process.exit(1); } try { data = JSON.parse(data); } catch(err) { - sys.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); + util.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); process.exit(1); } @@ -94,7 +94,7 @@ function compile(err, data) { process.exit(1); } else { if (!options.benchmark) { - sys.puts(output); + util.puts(output); } else { var duration = (+new Date) - start; console.log('TOTAL: ' + (duration) + 'ms'); @@ -103,9 +103,9 @@ function compile(err, data) { }); } catch (e) { if (e.stack) { - sys.error(e.stack); + util.error(e.stack); } else { - sys.error(e); + util.error(e); } } }; From 672a271cbe62f4c3f4496ddbbd04e5a859a4b254 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 14:41:13 -0400 Subject: [PATCH 12/16] Change field result, fix building height result. --- test/rendering/building_height.result | 20 ++++++++++++++++++++ test/rendering/field.result | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 test/rendering/building_height.result diff --git a/test/rendering/building_height.result b/test/rendering/building_height.result new file mode 100644 index 0000000..393778d --- /dev/null +++ b/test/rendering/building_height.result @@ -0,0 +1,20 @@ + + + + + + + + world + + + + + + + diff --git a/test/rendering/field.result b/test/rendering/field.result index f7ea5af..3e0b8d3 100644 --- a/test/rendering/field.result +++ b/test/rendering/field.result @@ -8,7 +8,7 @@ Date: Tue, 26 Jun 2012 15:54:36 -0400 Subject: [PATCH 13/16] Fix error checking around string operations, fix quoting, though it is still broken in the quoted + field + quoted case. --- lib/carto/tree/field.js | 2 +- lib/carto/tree/operation.js | 4 ++-- lib/carto/tree/quoted.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js index e18eb39..0a8981b 100644 --- a/lib/carto/tree/field.js +++ b/lib/carto/tree/field.js @@ -13,7 +13,7 @@ tree.Field.prototype = { return this; }, operate: function(op, other) { - return new tree.Quoted('', this.toString() + op + other.toString()); + return new tree.Quoted('', this.toString() + op + other.toString(true)); } }; diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index 1a26db4..5406cc5 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -31,9 +31,9 @@ tree.Operation.prototype.eval = function(env) { } if (a instanceof tree.Quoted || b instanceof tree.Quoted) { - if (this.op === '*' || this.op === '/') { + if (this.op !== '+') { env.error({ - message: 'One cannot add, subtract, divide, or multiply strings.', + message: "Can't subtract, divide, or multiply strings.", index: this.index, type: 'runtime', filename: this.filename diff --git a/lib/carto/tree/quoted.js b/lib/carto/tree/quoted.js index 67d0add..888a100 100644 --- a/lib/carto/tree/quoted.js +++ b/lib/carto/tree/quoted.js @@ -18,7 +18,7 @@ tree.Quoted.prototype = { operate: function(op, other) { if (other.is !== 'string') { return new tree.Quoted(this.quote, - this.toString() + op + other.toString()); + this.toString(true) + op + other.toString()); } else { return new tree.Quoted(this.quote, tree.operate(op, this.toString(), other.toString())); From a66fa6c462d4740b32329fb9162ea48c6546e477 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 17:03:34 -0400 Subject: [PATCH 14/16] Fix quoting situation around fields --- lib/carto/parser.js | 8 +++++--- lib/carto/tree/field.js | 3 ++- lib/carto/tree/operation.js | 4 +++- lib/carto/tree/quoted.js | 14 +++++++++----- test/rendering/field.mss | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/carto/parser.js b/lib/carto/parser.js index a17dd38..6aaf961 100644 --- a/lib/carto/parser.js +++ b/lib/carto/parser.js @@ -419,7 +419,7 @@ carto.Parser = function Parser(env) { if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return; var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); if (str) { - return new tree.Quoted(str[0], str[1] || str[2]); + return new tree.Quoted(true, str[1] || str[2]); } }, @@ -481,13 +481,15 @@ carto.Parser = function Parser(env) { return new tree.Call(name, args, i); } }, - arguments: function() { + // Arguments are comma-separated expressions + 'arguments': function() { var args = [], arg; while (arg = $(this.expression)) { args.push(arg); if (! $(',')) { break; } } + return args; }, literal: function() { @@ -511,7 +513,7 @@ carto.Parser = function Parser(env) { return new tree.Invalid(value, memo, 'Missing closing ) in URL.'); } else { return new tree.URL((value.value || value instanceof tree.Variable) ? - value : new tree.Quoted('"', value), imports.paths); + value : new tree.Quoted(true, value), imports.paths); } }, diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js index 0a8981b..249eaa4 100644 --- a/lib/carto/tree/field.js +++ b/lib/carto/tree/field.js @@ -13,7 +13,8 @@ tree.Field.prototype = { return this; }, operate: function(op, other) { - return new tree.Quoted('', this.toString() + op + other.toString(true)); + return new tree.Quoted(false, + this.toString() + op + other.toString(true)); } }; diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index 5406cc5..42b593d 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -30,7 +30,9 @@ tree.Operation.prototype.eval = function(env) { } } - if (a instanceof tree.Quoted || b instanceof tree.Quoted) { + // Only concatenate plain strings, because this is easily + // pre-processed + if (a instanceof tree.Quoted && b instanceof tree.Quoted) { if (this.op !== '+') { env.error({ message: "Can't subtract, divide, or multiply strings.", diff --git a/lib/carto/tree/quoted.js b/lib/carto/tree/quoted.js index 888a100..e5dfd46 100644 --- a/lib/carto/tree/quoted.js +++ b/lib/carto/tree/quoted.js @@ -1,14 +1,18 @@ (function(tree) { -tree.Quoted = function Quoted(str, content) { +tree.Quoted = function Quoted(ever_quote, content) { this.value = content || ''; - this.quote = str.charAt(0); + // ever_quote is basicaly a shutoff switch for this quoted string + // being a real expression. you can pass `false` as the first parameter + // to make sure that this never gets double-quoted. This is used with fields. + this.ever_quote = ever_quote; this.is = 'string'; }; + tree.Quoted.prototype = { toString: function(quotes) { var xmlvalue = this.value.replace(/\'/g, '''); - return (quotes === true) ? "'" + xmlvalue + "'" : this.value; + return (this.ever_quote && quotes === true) ? "'" + xmlvalue + "'" : this.value; }, 'eval': function() { @@ -17,10 +21,10 @@ tree.Quoted.prototype = { operate: function(op, other) { if (other.is !== 'string') { - return new tree.Quoted(this.quote, + return new tree.Quoted(false, this.toString(true) + op + other.toString()); } else { - return new tree.Quoted(this.quote, + return new tree.Quoted(true, tree.operate(op, this.toString(), other.toString())); } } diff --git a/test/rendering/field.mss b/test/rendering/field.mss index f5bb71f..28b8540 100644 --- a/test/rendering/field.mss +++ b/test/rendering/field.mss @@ -1,5 +1,5 @@ #world { - text-name: "hello " + [NAME]; + text-name: "hello " + [NAME] + ' hello'; text-size: 11; text-face-name: "Georgia Regular", "Arial Italic"; } From 651a650f2ea94116ceba70fc9e4952dcd46ee6dd Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 26 Jun 2012 17:54:01 -0400 Subject: [PATCH 15/16] Simplify quoted implementation, move field logic up to operate level, add Literal, add tests for transforms with fields. --- lib/carto/functions.js | 2 +- lib/carto/index.js | 2 +- lib/carto/parser.js | 4 ++-- lib/carto/tree/comment.js | 1 + lib/carto/tree/dimension.js | 8 ++----- lib/carto/tree/field.js | 4 ---- lib/carto/tree/literal.js | 17 ++++++++++++++ lib/carto/tree/operation.js | 32 +++++++++++++++----------- lib/carto/tree/quoted.js | 17 ++++---------- test/rendering/field.mss | 2 +- test/rendering/field.result | 2 +- test/rendering/transforms_field.mml | 14 +++++++++++ test/rendering/transforms_field.mss | 4 ++++ test/rendering/transforms_field.result | 20 ++++++++++++++++ 14 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 lib/carto/tree/literal.js create mode 100644 test/rendering/transforms_field.mml create mode 100644 test/rendering/transforms_field.mss create mode 100644 test/rendering/transforms_field.result diff --git a/lib/carto/functions.js b/lib/carto/functions.js index f569589..8f45a44 100644 --- a/lib/carto/functions.js +++ b/lib/carto/functions.js @@ -133,7 +133,7 @@ tree.functions = { .replace(/%[da]/, args[i].toString()); } str = str.replace(/%%/g, '%'); - return new tree.Quoted('"' + str + '"', str); + return new tree.Quoted(str); } }; diff --git a/lib/carto/index.js b/lib/carto/index.js index 34b3c89..9e73bcd 100644 --- a/lib/carto/index.js +++ b/lib/carto/index.js @@ -52,7 +52,7 @@ var carto = { [ 'call', 'color', 'comment', 'definition', 'dimension', 'directive', 'element', 'expression', 'filterset', 'filter', 'field', - 'keyword', 'layer', 'operation', 'quoted', 'imagefilter', + 'keyword', 'layer', 'literal', 'operation', 'quoted', 'imagefilter', 'reference', 'rule', 'ruleset', 'selector', 'style', 'url', 'value', 'variable', 'zoom', 'invalid', 'fontset' ].forEach(function(n) { diff --git a/lib/carto/parser.js b/lib/carto/parser.js index 6aaf961..733cae7 100644 --- a/lib/carto/parser.js +++ b/lib/carto/parser.js @@ -419,7 +419,7 @@ carto.Parser = function Parser(env) { if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return; var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); if (str) { - return new tree.Quoted(true, str[1] || str[2]); + return new tree.Quoted(str[1] || str[2]); } }, @@ -513,7 +513,7 @@ carto.Parser = function Parser(env) { return new tree.Invalid(value, memo, 'Missing closing ) in URL.'); } else { return new tree.URL((value.value || value instanceof tree.Variable) ? - value : new tree.Quoted(true, value), imports.paths); + value : new tree.Quoted(value), imports.paths); } }, diff --git a/lib/carto/tree/comment.js b/lib/carto/tree/comment.js index 98385c2..f7e0d90 100644 --- a/lib/carto/tree/comment.js +++ b/lib/carto/tree/comment.js @@ -4,6 +4,7 @@ tree.Comment = function Comment(value, silent) { this.value = value; this.silent = !!silent; }; + tree.Comment.prototype = { toString: function(env) { return ''; diff --git a/lib/carto/tree/dimension.js b/lib/carto/tree/dimension.js index 471e439..dfa7de2 100644 --- a/lib/carto/tree/dimension.js +++ b/lib/carto/tree/dimension.js @@ -35,12 +35,8 @@ tree.Dimension.prototype = { // conversions such that `100cm + 10mm` would yield // `101cm`. operate: function(op, other) { - if (other.is === 'field') { - return new tree.Quoted('', this.value + op + other.toString()); - } else { - return new tree.Dimension(tree.operate(op, this.value, other.value), - this.unit || other.unit); - } + return new tree.Dimension(tree.operate(op, this.value, other.value), + this.unit || other.unit); } }; diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js index 249eaa4..f6ebe8d 100644 --- a/lib/carto/tree/field.js +++ b/lib/carto/tree/field.js @@ -11,10 +11,6 @@ tree.Field.prototype = { }, 'eval': function() { return this; - }, - operate: function(op, other) { - return new tree.Quoted(false, - this.toString() + op + other.toString(true)); } }; diff --git a/lib/carto/tree/literal.js b/lib/carto/tree/literal.js new file mode 100644 index 0000000..5281608 --- /dev/null +++ b/lib/carto/tree/literal.js @@ -0,0 +1,17 @@ +(function(tree) { + +tree.Literal = function Field(content) { + this.value = content || ''; + this.is = 'field'; +}; + +tree.Literal.prototype = { + toString: function() { + return this.value; + }, + 'eval': function() { + return this; + } +}; + +})(require('../tree')); diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index 42b593d..000971e 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -1,11 +1,12 @@ (function(tree) { - tree.Operation = function Operation(op, operands, index) { this.op = op.trim(); this.operands = operands; this.index = index; + this.is = 'operation'; }; + tree.Operation.prototype.eval = function(env) { var a = this.operands[0].eval(env), b = this.operands[1].eval(env), @@ -32,19 +33,22 @@ tree.Operation.prototype.eval = function(env) { // Only concatenate plain strings, because this is easily // pre-processed - if (a instanceof tree.Quoted && b instanceof tree.Quoted) { - if (this.op !== '+') { - env.error({ - message: "Can't subtract, divide, or multiply strings.", - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } + if (a instanceof tree.Quoted && b instanceof tree.Quoted && this.op !== '+') { + env.error({ + message: "Can't subtract, divide, or multiply strings.", + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + + if (a instanceof tree.Field || b instanceof tree.Field || + a instanceof tree.Literal || b instanceof tree.Literal) { + return new tree.Literal(a.eval(env).toString(true) + this.op + b.eval(env).toString(true)); } return a.operate(this.op, b); diff --git a/lib/carto/tree/quoted.js b/lib/carto/tree/quoted.js index e5dfd46..4064098 100644 --- a/lib/carto/tree/quoted.js +++ b/lib/carto/tree/quoted.js @@ -1,18 +1,14 @@ (function(tree) { -tree.Quoted = function Quoted(ever_quote, content) { +tree.Quoted = function Quoted(content) { this.value = content || ''; - // ever_quote is basicaly a shutoff switch for this quoted string - // being a real expression. you can pass `false` as the first parameter - // to make sure that this never gets double-quoted. This is used with fields. - this.ever_quote = ever_quote; this.is = 'string'; }; tree.Quoted.prototype = { toString: function(quotes) { var xmlvalue = this.value.replace(/\'/g, '''); - return (this.ever_quote && quotes === true) ? "'" + xmlvalue + "'" : this.value; + return (quotes === true) ? "'" + xmlvalue + "'" : this.value; }, 'eval': function() { @@ -20,13 +16,8 @@ tree.Quoted.prototype = { }, operate: function(op, other) { - if (other.is !== 'string') { - return new tree.Quoted(false, - this.toString(true) + op + other.toString()); - } else { - return new tree.Quoted(true, - tree.operate(op, this.toString(), other.toString())); - } + return new tree.Quoted(true, + tree.operate(op, this.toString(), other.toString(this.contains_field))); } }; diff --git a/test/rendering/field.mss b/test/rendering/field.mss index 28b8540..5cd939b 100644 --- a/test/rendering/field.mss +++ b/test/rendering/field.mss @@ -1,5 +1,5 @@ #world { - text-name: "hello " + [NAME] + ' hello'; + text-name: "hello " + [NAME] + " hello"; text-size: 11; text-face-name: "Georgia Regular", "Arial Italic"; } diff --git a/test/rendering/field.result b/test/rendering/field.result index 3e0b8d3..716e378 100644 --- a/test/rendering/field.result +++ b/test/rendering/field.result @@ -8,7 +8,7 @@ + + + + + + + world + + + + + + + From d57006dd976315aa0222fb3f36e0c857baeb5842 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 27 Jun 2012 14:58:56 -0400 Subject: [PATCH 16/16] Add advanced field test, fixes for combining fields with colors that otherwise go back to mapnik --- lib/carto/tree/dimension.js | 2 +- lib/carto/tree/literal.js | 3 +++ lib/carto/tree/operation.js | 16 +++++++++++++++- test/rendering/field_advanced.mml | 23 +++++++++++++++++++++++ test/rendering/field_advanced.mss | 6 ++++++ test/rendering/field_advanced.result | 23 +++++++++++++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 test/rendering/field_advanced.mml create mode 100644 test/rendering/field_advanced.mss create mode 100644 test/rendering/field_advanced.result diff --git a/lib/carto/tree/dimension.js b/lib/carto/tree/dimension.js index dfa7de2..0fe8458 100644 --- a/lib/carto/tree/dimension.js +++ b/lib/carto/tree/dimension.js @@ -25,7 +25,7 @@ tree.Dimension.prototype = { return new tree.Color([this.value, this.value, this.value]); }, toString: function() { - return this.value; + return this.value.toString(); }, // In an operation between two Dimensions, diff --git a/lib/carto/tree/literal.js b/lib/carto/tree/literal.js index 5281608..135be6c 100644 --- a/lib/carto/tree/literal.js +++ b/lib/carto/tree/literal.js @@ -1,3 +1,6 @@ +// A literal is a literal string for Mapnik - the +// result of the combination of a `tree.Field` with any +// other type. (function(tree) { tree.Literal = function Field(content) { diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index 000971e..627063a 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -46,9 +46,23 @@ tree.Operation.prototype.eval = function(env) { }; } + // Fields, literals, dimensions, and quoted strings can be combined. if (a instanceof tree.Field || b instanceof tree.Field || a instanceof tree.Literal || b instanceof tree.Literal) { - return new tree.Literal(a.eval(env).toString(true) + this.op + b.eval(env).toString(true)); + if (a.is === 'color' || b.is === 'color') { + env.error({ + message: "Can't subtract, divide, or multiply colors in expressions.", + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } else { + return new tree.Literal(a.eval(env).toString(true) + this.op + b.eval(env).toString(true)); + } } return a.operate(this.op, b); diff --git a/test/rendering/field_advanced.mml b/test/rendering/field_advanced.mml new file mode 100644 index 0000000..12153a4 --- /dev/null +++ b/test/rendering/field_advanced.mml @@ -0,0 +1,23 @@ +{ + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Stylesheet": [ + "field_advanced.mss" + ], + "Layer": [{ + "name": "world", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }, + { + "class": "new", + "name": "countries", + "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over", + "Datasource": { + "file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip", + "type": "shape" + } + }] +} diff --git a/test/rendering/field_advanced.mss b/test/rendering/field_advanced.mss new file mode 100644 index 0000000..68f41e8 --- /dev/null +++ b/test/rendering/field_advanced.mss @@ -0,0 +1,6 @@ +#world { + text-name: "hello " + [NAME] + " hello" + 2; + text-size: 11; + text-face-name: "Georgia Regular", "Arial Italic"; +} + diff --git a/test/rendering/field_advanced.result b/test/rendering/field_advanced.result new file mode 100644 index 0000000..cf47ac3 --- /dev/null +++ b/test/rendering/field_advanced.result @@ -0,0 +1,23 @@ + + + + + + + + + + + world + + + + + + +