Skip to content
Browse files

Changing the way filters are compiled--dropping them into tokens. Add…

…ing lots of tests for parsing. Still mising compiler tests.
  • Loading branch information...
1 parent cb2e16f commit 76f82ac508be6e62cdeec7dc43251d8e8058f48c @paularmstrong paularmstrong committed Aug 9, 2011
Showing with 159 additions and 14 deletions.
  1. +36 −14 lib/parser.js
  2. +123 −0 tests/parser.test.js
View
50 lib/parser.js
@@ -11,12 +11,19 @@ var helpers = require('./helpers'),
LOGIC_TOKEN = 1,
VAR_TOKEN = 2;
+exports.TOKEN_TYPES = {
+ TEMPLATE: TEMPLATE,
+ LOGIC: LOGIC_TOKEN,
+ VAR: VAR_TOKEN
+};
+
exports.parse = function (data, tags) {
var rawtokens = data.trim().replace(/(^\n+)|(\n+$)/, "").split(/(\{%.*?%\}|\{\{.*?\}\}|\{#.*?#\})/),
stack = [[]],
index = 0,
i = 0, j = rawtokens.length,
- varname, token, parts, names, matches, tagname;
+ filters = [], filter_name,
+ varname, token, parts, part, names, matches, tagname;
for (i, j; i < j; i += 1) {
token = rawtokens[i];
@@ -27,13 +34,26 @@ exports.parse = function (data, tags) {
} else if (/^(\s|\n)+$/.test(token)) {
token = token.replace(/ +/, " ").replace(/\n+/, "\n");
} else if (variableRegexp.test(token)) {
- parts = token.replace(/^\{\{ *| *\}\}$/g, "").split(" ");
+ filters = [];
+ parts = token.replace(/^\{\{ *| *\}\}$/g, '').split('|');
varname = parts.shift();
+ for (part in parts) {
+ if (parts.hasOwnProperty(part)) {
+ part = parts[part];
+ filter_name = part.match(/^\w+/);
+ if (/\(/.test(part)) {
+ filters.push({ name: filter_name[0], args: part.replace(/^\w+\(|\'|\"|,|\)$/g, '').split(' ') });
+ } else {
+ filters.push({ name: filter_name[0], args: [] });
+ }
+ }
+ }
+
token = {
type: VAR_TOKEN,
name: varname,
- args: parts.length ? parts : []
+ filters: filters
};
} else if (logicRegexp.test(token)) {
parts = token.replace(/^\{% *| *%\}$/g, "").split(" ");
@@ -76,13 +96,15 @@ exports.parse = function (data, tags) {
};
function wrapFilter(variable, filter) {
- var matches = filter.match(/(\w*)\((.*)\)/),
- output = variable;
+ var output = variable,
+ args;
- if (matches && matches.length && filters.hasOwnProperty(matches[1])) {
- output = '__filters["' + matches[1] + '"](' + variable + ', ' + matches[2] + ')';
- } else {
- output = '__filters["' + filter + '"](' + variable + ')';
+ if (filters.hasOwnProperty(filter.name)) {
+ args = [variable];
+ if (filter.args.length) {
+ args.push(filters.args);
+ }
+ output = '__filters.' + filter.name + '.apply(this, [' + args.toString() + '])';
}
return output;
@@ -153,12 +175,12 @@ exports.compile = function compile(indent) {
}
if (token.type === VAR_TOKEN) {
- varOutput = token.name.split('|');
+ varOutput = token.name;
return code.push(
- 'if (' + check(varOutput[0]) + ') {'
- , ' __output.push(' + wrapFilters(varOutput[0], varOutput.slice(1)) + ');'
- , '} else if (' + check(varOutput[0], '__context') + ') {'
- , ' __output.push(' + wrapFilters(varOutput[0], varOutput.slice(1), '__context') + ');'
+ 'if (' + check(varOutput) + ') {'
+ , ' __output.push(' + wrapFilters(varOutput, token.filters) + ');'
+ , '} else if (' + check(varOutput, '__context') + ') {'
+ , ' __output.push(' + wrapFilters(varOutput, token.filters, '__context') + ');'
, '}'
);
}
View
123 tests/parser.test.js
@@ -0,0 +1,123 @@
+var testCase = require('nodeunit').testCase,
+ parser = require('../lib/parser');
+
+exports.Tags = testCase({
+ 'undefined tag throws error': function (test) {
+ test.throws(function () {
+ parser.parse('{% foobar %}', {});
+ }, Error);
+ test.done();
+ },
+
+ 'basic tag': function (test) {
+ var output = parser.parse('{% blah %}', { blah: {} });
+ test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: [], compile: {} }], output);
+
+ output = parser.parse('{% blah "foobar" %}', { blah: {} });
+ test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: ['"foobar"'], compile: {} }], output, 'args appended');
+
+ output = parser.parse('{% blah "foobar" barfoo %}', { blah: {} });
+ test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: ['"foobar"', 'barfoo'], compile: {} }], output, 'multiple args appended');
+
+ test.done();
+ },
+
+ 'basic tag with ends': function (test) {
+ var output = parser.parse('{% blah %}{% end %}', { blah: { ends: true } });
+ test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: [], compile: { ends: true }, tokens: [] }], output);
+ test.done();
+ },
+
+ 'throws if requires end but no end found': function (test) {
+ test.throws(function () {
+ parser.parse('{% blah %}', { blah: { ends: true }});
+ }, Error);
+ test.done();
+ },
+
+ 'throws if not end but end found': function (test) {
+ test.throws(function () {
+ parser.parse('{% blah %}{% end %}', { blah: {}});
+ }, Error);
+ test.done();
+ },
+
+ 'tag with contents': function (test) {
+ var output = parser.parse('{% blah %}hello{% end %}', { blah: { ends: true } });
+ test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: [], compile: { ends: true }, tokens: ['hello'] }], output);
+ test.done();
+ }
+});
+
+exports.Comments = testCase({
+ 'empty strings are ignored': function (test) {
+ var output = parser.parse('');
+ test.deepEqual([], output);
+
+ output = parser.parse(' ');
+ test.deepEqual([], output);
+
+ output = parser.parse(' \n');
+ test.deepEqual([], output);
+
+ test.done();
+ },
+
+ 'comments are ignored': function (test) {
+ var output = parser.parse('{# foobar #}');
+ test.deepEqual([], output);
+ test.done();
+ }
+});
+
+exports.Variable = testCase({
+ 'basic variable': function (test) {
+ var output = parser.parse('{{ foobar }}');
+ test.deepEqual([{ type: parser.TOKEN_TYPES.VAR, name: 'foobar', filters: [] }], output, 'with spaces');
+
+ output = parser.parse('{{foobar}}');
+ test.deepEqual([{ type: parser.TOKEN_TYPES.VAR, name: 'foobar', filters: [] }], output, 'without spaces');
+
+ test.done();
+ },
+
+ 'dot-notation variable': function (test) {
+ var output = parser.parse('{{ foo.bar }}');
+ test.deepEqual([{ type: parser.TOKEN_TYPES.VAR, name: 'foo.bar', filters: [] }], output);
+ test.done();
+ },
+
+ 'variable with filter': function (test) {
+ var output = parser.parse('{{ foobar|awesome }}');
+ test.deepEqual([{ type: parser.TOKEN_TYPES.VAR, name: 'foobar', filters: [{ name: 'awesome', args: [] }] }], output, 'filter by name');
+
+ output = parser.parse('{{ foobar|awesome("param", 2) }}');
+ test.deepEqual([{ type: parser.TOKEN_TYPES.VAR, name: 'foobar', filters: [{ name: 'awesome', args: ['param', 2] }] }], output, 'filter with params');
+
+ test.done();
+ },
+
+ 'multiple filters': function (test) {
+ var output = parser.parse('{{ foobar|baz(1)|rad|awesome("param", 2) }}');
+ test.deepEqual([{ type: parser.TOKEN_TYPES.VAR, name: 'foobar', filters: [
+ { name: 'baz', args: [1] },
+ { name: 'rad', args: [] },
+ { name: 'awesome', args: ['param', 2] }
+ ] }], output);
+
+ test.done();
+ },
+
+ 'filters do not carry over': function (test) {
+ var output = parser.parse('{{ foo|baz }}{{ bar }}');
+ test.deepEqual([
+ { type: parser.TOKEN_TYPES.VAR, name: 'foo', filters: [{ name: 'baz', args: [] }] },
+ { type: parser.TOKEN_TYPES.VAR, name: 'bar', filters: [] }
+ ], output);
+ test.done();
+ }
+});
+
+exports.Compiling = testCase({
+ // TODO: fill in tests for compiling
+});

0 comments on commit 76f82ac

Please sign in to comment.
Something went wrong with that request. Please try again.