This commit is contained in:
harttle
2016-06-19 00:46:29 +08:00
parent 4595e2872e
commit 1bc6d9355a
7 changed files with 57 additions and 58 deletions
+4 -4
View File
@@ -6,12 +6,12 @@ var doubleQuoted = /"[^"]*"/;
var quoteBalanced = new RegExp(`(?:${singleQuoted.source}|${doubleQuoted.source}|[^'"])*`);
var number = /(?:-?\d+\.?\d*|\.?\d+)/;
var bool = /true|false/i;
var bool = /true|false/;
var identifier = /[a-zA-Z_$][a-zA-Z_$0-9]*/;
var subscript = /\[\d+\]/;
var quoted = new RegExp(`(?:${singleQuoted.source}|${doubleQuoted.source})`);
var literal = new RegExp(`(?:${quoted.source}|${bool.source}|${number.source})`, 'i');
var literal = new RegExp(`(?:${quoted.source}|${bool.source}|${number.source})`);
var variable = new RegExp(`${identifier.source}(?:\\.${identifier.source}|${subscript.source})*`);
// range related
@@ -19,10 +19,10 @@ var rangeLimit = new RegExp(`(?:${variable.source}|${number.source})`);
var range = new RegExp(`\\(${rangeLimit.source}\\.\\.${rangeLimit.source}\\)`);
var rangeCapture = new RegExp(`\\((${rangeLimit.source})\\.\\.(${rangeLimit.source})\\)`);
var value = new RegExp(`(?:${literal.source}|${variable.source}|${range.source})`, 'i');
var value = new RegExp(`(?:${literal.source}|${variable.source}|${range.source})`);
// hash related
var hash = new RegExp(`(?:${identifier.source})\\s*:\\s*(?:${value.source})`, 'g');
var hash = new RegExp(`(?:${identifier.source})\\s*:\\s*(?:${value.source})`);
var hashCapture = new RegExp(`(${identifier.source})\\s*:\\s*(${value.source})`, 'g');
var tagLine = new RegExp(`^\\s*(${identifier.source})\\s*(.*)\\s*$`);
+23 -23
View File
@@ -2,21 +2,6 @@ const lexical = require('./lexical.js');
const Exp = require('./expression.js');
const TokenizationError = require('./error.js').TokenizationError;
var _tagInstance = {
render: function(scope, register) {
var reg = register[this.name];
if(!reg) reg = register[this.name] = {};
var obj = hash(this.token.args, scope);
return this.tagImpl.render(scope, obj, reg) || '';
},
parse: function(tokens){
if(this.tagImpl.parse){
this.tagImpl.parse(this.token, tokens);
}
return this;
}
};
function hash(markup, scope) {
var obj = {};
lexical.hashCapture.lastIndex = 0;
@@ -31,6 +16,27 @@ function hash(markup, scope) {
module.exports = function() {
var tagImpls = {};
var _tagInstance = {
render: function(scope, register) {
var reg = register[this.name];
if(!reg) reg = register[this.name] = {};
var obj = hash(this.token.args, scope);
return this.tagImpl.render(scope, obj, reg) || '';
},
parse: function(token, tokens){
this.type = 'tag';
this.token = token;
this.name = token.name;
var tagImpl = tagImpls[this.name];
if (!tagImpl) throw new Error(`tag ${this.name} not found`);
this.tagImpl = Object.create(tagImpl);
if(this.tagImpl.parse){
this.tagImpl.parse(token, tokens);
}
}
};
function register(name, tag) {
if (typeof tag.render !== 'function') {
throw new Error(`expect ${name}.render to be a function`);
@@ -38,15 +44,9 @@ module.exports = function() {
tagImpls[name] = tag;
}
function construct(token) {
var tagImpl = tagImpls[token.name];
if (!tagImpl) throw new Error(`tag ${token.name} not found`);
function construct(token, tokens) {
var instance = Object.create(_tagInstance);
instance.token = token;
instance.type = 'tag';
instance.name = token.name;
instance.tagImpl = Object.create(tagImpl);
instance.parse(token, tokens);
return instance;
}
+1 -1
View File
@@ -20,7 +20,7 @@ module.exports = function(liquid) {
}
if (!this.candidates.length){
throw new Error(`illegal tag: ${tagToken.raw}`);
throw new Error(`empty candidates: ${tagToken.raw}`);
}
},
+7 -26
View File
@@ -3,23 +3,11 @@ var lexical = Liquid.lexical;
module.exports = function(liquid) {
liquid.registerTag('unless', {
parse: function(tagToken, remainTokens) {
this.branches = [];
this.elseTemplates = [];
var p, stream = liquid.parseStream(remainTokens)
.onStart(x => this.branches.push({
cond: tagToken.args,
templates: p = []
}))
.onTag('elsif', token => {
if (!this.branches[token.args]) {
this.branches.push({
cond: token.args,
templates: p = []
});
}
.onStart(x => {
p = this.templates = [];
this.cond = tagToken.args;
})
.onTag('else', token => this.elseTemplates = p = [])
.onTag('endunless', token => stream.stop())
@@ -32,17 +20,10 @@ module.exports = function(liquid) {
},
render: function(scope, hash) {
for (var i = 0; i < this.branches.length; i++) {
var branch = this.branches[i];
var cond = Liquid.evalExp(branch.cond, scope);
cond = Liquid.isTruthy(cond);
if (i === 0) cond = !cond;
if (cond) {
return liquid.renderTemplates(branch.templates, scope);
}
}
return liquid.renderTemplates(this.elseTemplates, scope);
var cond = Liquid.evalExp(this.cond, scope);
return Liquid.isFalsy(cond) ?
liquid.renderTemplates(this.templates, scope) :
liquid.renderTemplates(this.elseTemplates, scope);
}
});
};
+1 -1
View File
@@ -77,7 +77,7 @@ module.exports = function(Tag, Filter) {
function parseTag(token, tokens) {
if (token.name === 'continue' || token.name === 'break') return token;
return Tag.construct(token).parse(tokens);
return Tag.construct(token, tokens);
}
function parseOutput(str) {
+3 -3
View File
@@ -23,7 +23,7 @@ describe('tag', function() {
type: 'tag',
value: 'foo',
name: 'foo'
});
}, []);
}).to.throw(/tag foo not found/);
});
@@ -52,7 +52,7 @@ describe('tag', function() {
type: 'tag',
value: 'foo',
name: 'foo'
}).render(scope, {});
}, []).render(scope, {});
expect(spy).to.have.been.called;
});
@@ -68,7 +68,7 @@ describe('tag', function() {
name: 'foo',
args: 'aa:foo bb: arr[0] cc: 2.3'
};
tag.construct(token).render(scope, {});
tag.construct(token, []).render(scope, {});
expect(spy).to.have.been.calledWithMatch(scope, {
aa: 'bar',
bb: 2,
+18
View File
@@ -27,11 +27,13 @@ describe('tags', function() {
emptyArray: []
};
});
it('should support assign', function() {
test('{% assign foo="bar" %}{{foo}}', 'bar');
test('{% assign foo=(1..3) %}{{foo}}', '[1,2,3]');
test('{% assign foo="a b" | capitalize | split: " " | first %}{{foo}}', 'A');
});
it('should support case', function() {
testThrow('{% case "foo"%}', /{% case "foo"%} not closed/);
test('{% case "foo"%}' +
@@ -69,10 +71,22 @@ describe('tags', function() {
testThrow('{% capture = %}{%endcapture%}', /= not valid identifier/);
});
it('should throw when for capture closed', function() {
testThrow('{%capture c%}{{c}}', /tag .* not closed/);
});
it('should support for', function() {
test('{%for c in alpha%}{{c}}{%endfor%}', 'abc');
});
it('should throw when for not closed', function() {
testThrow('{%for c in alpha%}{{c}}', /tag .* not closed/);
});
it('should support for else', function() {
test('{%for c in ""%}a{%else%}b{%endfor%}', 'b');
});
it('should support for with forloop', function() {
src = '{%for c in alpha%}' +
'{{forloop.first}}.{{forloop.index}}.{{forloop.index0}}.' +
@@ -117,6 +131,10 @@ describe('tags', function() {
test(src + src + src + src, '1231');
});
it('should throw when cycle candidates empty', function() {
testThrow('{%cycle%}', /empty candidates/);
});
it('should support cycle in for block', function() {
src = '{% for i in (1..5) %}{% cycle one, "e"%}{% endfor %}';
test(src, '1e1e1');