293 lines
8.2 KiB
JavaScript
293 lines
8.2 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
function installCompat() {
|
||
|
'use strict';
|
||
|
|
||
|
/* eslint-disable camelcase */
|
||
|
|
||
|
// This must be called like `nunjucks.installCompat` so that `this`
|
||
|
// references the nunjucks instance
|
||
|
var runtime = this.runtime;
|
||
|
var lib = this.lib;
|
||
|
// Handle slim case where these 'modules' are excluded from the built source
|
||
|
var Compiler = this.compiler.Compiler;
|
||
|
var Parser = this.parser.Parser;
|
||
|
var nodes = this.nodes;
|
||
|
var lexer = this.lexer;
|
||
|
var orig_contextOrFrameLookup = runtime.contextOrFrameLookup;
|
||
|
var orig_memberLookup = runtime.memberLookup;
|
||
|
var orig_Compiler_assertType;
|
||
|
var orig_Parser_parseAggregate;
|
||
|
if (Compiler) {
|
||
|
orig_Compiler_assertType = Compiler.prototype.assertType;
|
||
|
}
|
||
|
if (Parser) {
|
||
|
orig_Parser_parseAggregate = Parser.prototype.parseAggregate;
|
||
|
}
|
||
|
function uninstall() {
|
||
|
runtime.contextOrFrameLookup = orig_contextOrFrameLookup;
|
||
|
runtime.memberLookup = orig_memberLookup;
|
||
|
if (Compiler) {
|
||
|
Compiler.prototype.assertType = orig_Compiler_assertType;
|
||
|
}
|
||
|
if (Parser) {
|
||
|
Parser.prototype.parseAggregate = orig_Parser_parseAggregate;
|
||
|
}
|
||
|
}
|
||
|
runtime.contextOrFrameLookup = function contextOrFrameLookup(context, frame, key) {
|
||
|
var val = orig_contextOrFrameLookup.apply(this, arguments);
|
||
|
if (val !== undefined) {
|
||
|
return val;
|
||
|
}
|
||
|
switch (key) {
|
||
|
case 'True':
|
||
|
return true;
|
||
|
case 'False':
|
||
|
return false;
|
||
|
case 'None':
|
||
|
return null;
|
||
|
default:
|
||
|
return undefined;
|
||
|
}
|
||
|
};
|
||
|
function getTokensState(tokens) {
|
||
|
return {
|
||
|
index: tokens.index,
|
||
|
lineno: tokens.lineno,
|
||
|
colno: tokens.colno
|
||
|
};
|
||
|
}
|
||
|
if (process.env.BUILD_TYPE !== 'SLIM' && nodes && Compiler && Parser) {
|
||
|
// i.e., not slim mode
|
||
|
var Slice = nodes.Node.extend('Slice', {
|
||
|
fields: ['start', 'stop', 'step'],
|
||
|
init: function init(lineno, colno, start, stop, step) {
|
||
|
start = start || new nodes.Literal(lineno, colno, null);
|
||
|
stop = stop || new nodes.Literal(lineno, colno, null);
|
||
|
step = step || new nodes.Literal(lineno, colno, 1);
|
||
|
this.parent(lineno, colno, start, stop, step);
|
||
|
}
|
||
|
});
|
||
|
Compiler.prototype.assertType = function assertType(node) {
|
||
|
if (node instanceof Slice) {
|
||
|
return;
|
||
|
}
|
||
|
orig_Compiler_assertType.apply(this, arguments);
|
||
|
};
|
||
|
Compiler.prototype.compileSlice = function compileSlice(node, frame) {
|
||
|
this._emit('(');
|
||
|
this._compileExpression(node.start, frame);
|
||
|
this._emit('),(');
|
||
|
this._compileExpression(node.stop, frame);
|
||
|
this._emit('),(');
|
||
|
this._compileExpression(node.step, frame);
|
||
|
this._emit(')');
|
||
|
};
|
||
|
Parser.prototype.parseAggregate = function parseAggregate() {
|
||
|
var _this = this;
|
||
|
var origState = getTokensState(this.tokens);
|
||
|
// Set back one accounting for opening bracket/parens
|
||
|
origState.colno--;
|
||
|
origState.index--;
|
||
|
try {
|
||
|
return orig_Parser_parseAggregate.apply(this);
|
||
|
} catch (e) {
|
||
|
var errState = getTokensState(this.tokens);
|
||
|
var rethrow = function rethrow() {
|
||
|
lib._assign(_this.tokens, errState);
|
||
|
return e;
|
||
|
};
|
||
|
|
||
|
// Reset to state before original parseAggregate called
|
||
|
lib._assign(this.tokens, origState);
|
||
|
this.peeked = false;
|
||
|
var tok = this.peekToken();
|
||
|
if (tok.type !== lexer.TOKEN_LEFT_BRACKET) {
|
||
|
throw rethrow();
|
||
|
} else {
|
||
|
this.nextToken();
|
||
|
}
|
||
|
var node = new Slice(tok.lineno, tok.colno);
|
||
|
|
||
|
// If we don't encounter a colon while parsing, this is not a slice,
|
||
|
// so re-raise the original exception.
|
||
|
var isSlice = false;
|
||
|
for (var i = 0; i <= node.fields.length; i++) {
|
||
|
if (this.skip(lexer.TOKEN_RIGHT_BRACKET)) {
|
||
|
break;
|
||
|
}
|
||
|
if (i === node.fields.length) {
|
||
|
if (isSlice) {
|
||
|
this.fail('parseSlice: too many slice components', tok.lineno, tok.colno);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (this.skip(lexer.TOKEN_COLON)) {
|
||
|
isSlice = true;
|
||
|
} else {
|
||
|
var field = node.fields[i];
|
||
|
node[field] = this.parseExpression();
|
||
|
isSlice = this.skip(lexer.TOKEN_COLON) || isSlice;
|
||
|
}
|
||
|
}
|
||
|
if (!isSlice) {
|
||
|
throw rethrow();
|
||
|
}
|
||
|
return new nodes.Array(tok.lineno, tok.colno, [node]);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
function sliceLookup(obj, start, stop, step) {
|
||
|
obj = obj || [];
|
||
|
if (start === null) {
|
||
|
start = step < 0 ? obj.length - 1 : 0;
|
||
|
}
|
||
|
if (stop === null) {
|
||
|
stop = step < 0 ? -1 : obj.length;
|
||
|
} else if (stop < 0) {
|
||
|
stop += obj.length;
|
||
|
}
|
||
|
if (start < 0) {
|
||
|
start += obj.length;
|
||
|
}
|
||
|
var results = [];
|
||
|
for (var i = start;; i += step) {
|
||
|
if (i < 0 || i > obj.length) {
|
||
|
break;
|
||
|
}
|
||
|
if (step > 0 && i >= stop) {
|
||
|
break;
|
||
|
}
|
||
|
if (step < 0 && i <= stop) {
|
||
|
break;
|
||
|
}
|
||
|
results.push(runtime.memberLookup(obj, i));
|
||
|
}
|
||
|
return results;
|
||
|
}
|
||
|
function hasOwnProp(obj, key) {
|
||
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
||
|
}
|
||
|
var ARRAY_MEMBERS = {
|
||
|
pop: function pop(index) {
|
||
|
if (index === undefined) {
|
||
|
return this.pop();
|
||
|
}
|
||
|
if (index >= this.length || index < 0) {
|
||
|
throw new Error('KeyError');
|
||
|
}
|
||
|
return this.splice(index, 1);
|
||
|
},
|
||
|
append: function append(element) {
|
||
|
return this.push(element);
|
||
|
},
|
||
|
remove: function remove(element) {
|
||
|
for (var i = 0; i < this.length; i++) {
|
||
|
if (this[i] === element) {
|
||
|
return this.splice(i, 1);
|
||
|
}
|
||
|
}
|
||
|
throw new Error('ValueError');
|
||
|
},
|
||
|
count: function count(element) {
|
||
|
var count = 0;
|
||
|
for (var i = 0; i < this.length; i++) {
|
||
|
if (this[i] === element) {
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
},
|
||
|
index: function index(element) {
|
||
|
var i;
|
||
|
if ((i = this.indexOf(element)) === -1) {
|
||
|
throw new Error('ValueError');
|
||
|
}
|
||
|
return i;
|
||
|
},
|
||
|
find: function find(element) {
|
||
|
return this.indexOf(element);
|
||
|
},
|
||
|
insert: function insert(index, elem) {
|
||
|
return this.splice(index, 0, elem);
|
||
|
}
|
||
|
};
|
||
|
var OBJECT_MEMBERS = {
|
||
|
items: function items() {
|
||
|
return lib._entries(this);
|
||
|
},
|
||
|
values: function values() {
|
||
|
return lib._values(this);
|
||
|
},
|
||
|
keys: function keys() {
|
||
|
return lib.keys(this);
|
||
|
},
|
||
|
get: function get(key, def) {
|
||
|
var output = this[key];
|
||
|
if (output === undefined) {
|
||
|
output = def;
|
||
|
}
|
||
|
return output;
|
||
|
},
|
||
|
has_key: function has_key(key) {
|
||
|
return hasOwnProp(this, key);
|
||
|
},
|
||
|
pop: function pop(key, def) {
|
||
|
var output = this[key];
|
||
|
if (output === undefined && def !== undefined) {
|
||
|
output = def;
|
||
|
} else if (output === undefined) {
|
||
|
throw new Error('KeyError');
|
||
|
} else {
|
||
|
delete this[key];
|
||
|
}
|
||
|
return output;
|
||
|
},
|
||
|
popitem: function popitem() {
|
||
|
var keys = lib.keys(this);
|
||
|
if (!keys.length) {
|
||
|
throw new Error('KeyError');
|
||
|
}
|
||
|
var k = keys[0];
|
||
|
var val = this[k];
|
||
|
delete this[k];
|
||
|
return [k, val];
|
||
|
},
|
||
|
setdefault: function setdefault(key, def) {
|
||
|
if (def === void 0) {
|
||
|
def = null;
|
||
|
}
|
||
|
if (!(key in this)) {
|
||
|
this[key] = def;
|
||
|
}
|
||
|
return this[key];
|
||
|
},
|
||
|
update: function update(kwargs) {
|
||
|
lib._assign(this, kwargs);
|
||
|
return null; // Always returns None
|
||
|
}
|
||
|
};
|
||
|
|
||
|
OBJECT_MEMBERS.iteritems = OBJECT_MEMBERS.items;
|
||
|
OBJECT_MEMBERS.itervalues = OBJECT_MEMBERS.values;
|
||
|
OBJECT_MEMBERS.iterkeys = OBJECT_MEMBERS.keys;
|
||
|
runtime.memberLookup = function memberLookup(obj, val, autoescape) {
|
||
|
if (arguments.length === 4) {
|
||
|
return sliceLookup.apply(this, arguments);
|
||
|
}
|
||
|
obj = obj || {};
|
||
|
|
||
|
// If the object is an object, return any of the methods that Python would
|
||
|
// otherwise provide.
|
||
|
if (lib.isArray(obj) && hasOwnProp(ARRAY_MEMBERS, val)) {
|
||
|
return ARRAY_MEMBERS[val].bind(obj);
|
||
|
}
|
||
|
if (lib.isObject(obj) && hasOwnProp(OBJECT_MEMBERS, val)) {
|
||
|
return OBJECT_MEMBERS[val].bind(obj);
|
||
|
}
|
||
|
return orig_memberLookup.apply(this, arguments);
|
||
|
};
|
||
|
return uninstall;
|
||
|
}
|
||
|
module.exports = installCompat;
|