Skip to content

Instantly share code, notes, and snippets.

@atian25
Created January 5, 2016 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atian25/6388f6853ef70ac01d58 to your computer and use it in GitHub Desktop.
Save atian25/6388f6853ef70ac01d58 to your computer and use it in GitHub Desktop.
'use strict';
var swig = require('swig');
var expect = require('expect.js');
describe('test', function() {
var compileFn = function(compiler, args, content, parents, options, blockName) {
return ';_output += "<div"' + JSON.stringify(args) + '+">";' + compiler(content, parents, options, blockName) + ';' + '_output += "</div>";';
};
var parseFn = function(str, line, parser, types, options) {
function parseToken(token){
var _t = types;
var self = this,
fn = self._parsers[token.type] || self._parsers['*'],
match = token.match,
prevToken = self.prevToken,
prevTokenType = prevToken ? prevToken.type : null,
lastState = (self.state.length) ? self.state[self.state.length - 1] : null,
temp;
if (lastState && prevToken &&
lastState === _t.FILTER &&
prevTokenType === _t.FILTER &&
token.type !== _t.PARENCLOSE &&
token.type !== _t.COMMA &&
token.type !== _t.OPERATOR &&
token.type !== _t.FILTER &&
token.type !== _t.FILTEREMPTY) {
self.out.push(', ');
}
if (lastState && lastState === _t.METHODOPEN) {
self.state.pop();
if (token.type !== _t.PARENCLOSE) {
self.out.push(', ');
}
}
switch (token.type) {
case _t.WHITESPACE:
break;
case _t.STRING:
self.filterApplyIdx.push(self.out.length);
self.out.push(match.replace(/\\/g, '\\\\'));
break;
case _t.NUMBER:
case _t.BOOL:
self.filterApplyIdx.push(self.out.length);
self.out.push(match);
break;
case _t.FILTER:
if (!self.filters.hasOwnProperty(match) || typeof self.filters[match] !== "function") {
utils.throwError('Invalid filter "' + match + '"', self.line, self.filename);
}
self.escape = self.filters[match].safe ? false : self.escape;
self.out.splice(self.filterApplyIdx[self.filterApplyIdx.length - 1], 0, '_filters["' + match + '"](');
self.state.push(token.type);
break;
case _t.FILTEREMPTY:
if (!self.filters.hasOwnProperty(match) || typeof self.filters[match] !== "function") {
utils.throwError('Invalid filter "' + match + '"', self.line, self.filename);
}
self.escape = self.filters[match].safe ? false : self.escape;
self.out.splice(self.filterApplyIdx[self.filterApplyIdx.length - 1], 0, '_filters["' + match + '"](');
self.out.push(')');
break;
case _t.FUNCTION:
case _t.FUNCTIONEMPTY:
self.out.push('((typeof _ctx.' + match + ' !== "undefined") ? _ctx.' + match +
' : ((typeof ' + match + ' !== "undefined") ? ' + match +
' : _fn))(');
self.escape = false;
if (token.type === _t.FUNCTIONEMPTY) {
self.out[self.out.length - 1] = self.out[self.out.length - 1] + ')';
} else {
self.state.push(token.type);
}
self.filterApplyIdx.push(self.out.length - 1);
break;
case _t.PARENOPEN:
self.state.push(token.type);
if (self.filterApplyIdx.length) {
self.out.splice(self.filterApplyIdx[self.filterApplyIdx.length - 1], 0, '(');
if (prevToken && prevTokenType === _t.VAR) {
temp = prevToken.match.split('.').slice(0, -1);
self.out.push(' || _fn).call(' + self.checkMatch(temp));
self.state.push(_t.METHODOPEN);
self.escape = false;
} else {
self.out.push(' || _fn)(');
}
self.filterApplyIdx.push(self.out.length - 3);
} else {
self.out.push('(');
self.filterApplyIdx.push(self.out.length - 1);
}
break;
case _t.PARENCLOSE:
temp = self.state.pop();
if (temp !== _t.PARENOPEN && temp !== _t.FUNCTION && temp !== _t.FILTER) {
utils.throwError('Mismatched nesting state', self.line, self.filename);
}
self.out.push(')');
// Once off the previous entry
self.filterApplyIdx.pop();
if (temp !== _t.FILTER) {
// Once for the open paren
self.filterApplyIdx.pop();
}
break;
case _t.COMMA:
if (lastState !== _t.FUNCTION &&
lastState !== _t.FILTER &&
lastState !== _t.ARRAYOPEN &&
lastState !== _t.CURLYOPEN &&
lastState !== _t.PARENOPEN &&
lastState !== _t.COLON) {
utils.throwError('Unexpected comma', self.line, self.filename);
}
if (lastState === _t.COLON) {
self.state.pop();
}
self.out.push(', ');
self.filterApplyIdx.pop();
break;
case _t.LOGIC:
case _t.COMPARATOR:
if (!prevToken ||
prevTokenType === _t.COMMA ||
prevTokenType === token.type ||
prevTokenType === _t.BRACKETOPEN ||
prevTokenType === _t.CURLYOPEN ||
prevTokenType === _t.PARENOPEN ||
prevTokenType === _t.FUNCTION) {
utils.throwError('Unexpected logic', self.line, self.filename);
}
self.out.push(token.match);
break;
case _t.NOT:
self.out.push(token.match);
break;
case _t.VAR:
self.parseVar(token, match, lastState);
break;
case _t.BRACKETOPEN:
if (!prevToken ||
(prevTokenType !== _t.VAR &&
prevTokenType !== _t.BRACKETCLOSE &&
prevTokenType !== _t.PARENCLOSE)) {
self.state.push(_t.ARRAYOPEN);
self.filterApplyIdx.push(self.out.length);
} else {
self.state.push(token.type);
}
self.out.push('[');
break;
case _t.BRACKETCLOSE:
temp = self.state.pop();
if (temp !== _t.BRACKETOPEN && temp !== _t.ARRAYOPEN) {
utils.throwError('Unexpected closing square bracket', self.line, self.filename);
}
self.out.push(']');
self.filterApplyIdx.pop();
break;
case _t.CURLYOPEN:
self.state.push(token.type);
self.out.push('{');
self.filterApplyIdx.push(self.out.length - 1);
break;
case _t.COLON:
if (lastState !== _t.CURLYOPEN) {
utils.throwError('Unexpected colon', self.line, self.filename);
}
self.state.push(token.type);
self.out.push(':');
self.filterApplyIdx.pop();
break;
case _t.CURLYCLOSE:
if (lastState === _t.COLON) {
self.state.pop();
}
if (self.state.pop() !== _t.CURLYOPEN) {
utils.throwError('Unexpected closing curly brace', self.line, self.filename);
}
self.out.push('}');
self.filterApplyIdx.pop();
break;
case _t.DOTKEY:
if (!prevToken || (
prevTokenType !== _t.VAR &&
prevTokenType !== _t.BRACKETCLOSE &&
prevTokenType !== _t.DOTKEY &&
prevTokenType !== _t.PARENCLOSE &&
prevTokenType !== _t.FUNCTIONEMPTY &&
prevTokenType !== _t.FILTEREMPTY &&
prevTokenType !== _t.CURLYCLOSE
)) {
utils.throwError('Unexpected key "' + match + '"', self.line, self.filename);
}
self.out.push('.' + match);
break;
case _t.OPERATOR:
self.out.push(' ' + match + ' ');
self.filterApplyIdx.pop();
break;
}
}
parser.on('start', function() {
this
// called when a parse starts
});
parser.on('*', function(token) {
var str = token.match;
// 自定义时貌似只能拷贝parseToken实现出来, 去掉fn.call的判断
// this.out.push(parseToken.call(this, token));
return true;
// called on the match of any token at all ("*")
});
parser.on('end', function() {
this.out
// called when a parse ends
});
return true; // parser is good to go
};
swig.setTag('test', parseFn, compileFn, true, false);
var context = {
locals: {
clz: 'test',
foo: {
bar: 'bar'
},
bool: false,
html: '<img src=>',
jsonStr: JSON.stringify({ a: 'b' })
}
};
it('should parse: ', function() {
var c = '{% test a=!bool.test["a"]|first|last %}content{% endtest %}';
expect(swig.render(c, context)).to.be.equal('<div>content</div>');
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment