1"use strict"; 2 3function installCompat() { 4 'use strict'; 5 6 /* eslint-disable camelcase */ 7 8 // This must be called like `nunjucks.installCompat` so that `this` 9 // references the nunjucks instance 10 var runtime = this.runtime; 11 var lib = this.lib; 12 // Handle slim case where these 'modules' are excluded from the built source 13 var Compiler = this.compiler.Compiler; 14 var Parser = this.parser.Parser; 15 var nodes = this.nodes; 16 var lexer = this.lexer; 17 var orig_contextOrFrameLookup = runtime.contextOrFrameLookup; 18 var orig_memberLookup = runtime.memberLookup; 19 var orig_Compiler_assertType; 20 var orig_Parser_parseAggregate; 21 if (Compiler) { 22 orig_Compiler_assertType = Compiler.prototype.assertType; 23 } 24 if (Parser) { 25 orig_Parser_parseAggregate = Parser.prototype.parseAggregate; 26 } 27 function uninstall() { 28 runtime.contextOrFrameLookup = orig_contextOrFrameLookup; 29 runtime.memberLookup = orig_memberLookup; 30 if (Compiler) { 31 Compiler.prototype.assertType = orig_Compiler_assertType; 32 } 33 if (Parser) { 34 Parser.prototype.parseAggregate = orig_Parser_parseAggregate; 35 } 36 } 37 runtime.contextOrFrameLookup = function contextOrFrameLookup(context, frame, key) { 38 var val = orig_contextOrFrameLookup.apply(this, arguments); 39 if (val !== undefined) { 40 return val; 41 } 42 switch (key) { 43 case 'True': 44 return true; 45 case 'False': 46 return false; 47 case 'None': 48 return null; 49 default: 50 return undefined; 51 } 52 }; 53 function getTokensState(tokens) { 54 return { 55 index: tokens.index, 56 lineno: tokens.lineno, 57 colno: tokens.colno 58 }; 59 } 60 if (process.env.BUILD_TYPE !== 'SLIM' && nodes && Compiler && Parser) { 61 // i.e., not slim mode 62 var Slice = nodes.Node.extend('Slice', { 63 fields: ['start', 'stop', 'step'], 64 init: function init(lineno, colno, start, stop, step) { 65 start = start || new nodes.Literal(lineno, colno, null); 66 stop = stop || new nodes.Literal(lineno, colno, null); 67 step = step || new nodes.Literal(lineno, colno, 1); 68 this.parent(lineno, colno, start, stop, step); 69 } 70 }); 71 Compiler.prototype.assertType = function assertType(node) { 72 if (node instanceof Slice) { 73 return; 74 } 75 orig_Compiler_assertType.apply(this, arguments); 76 }; 77 Compiler.prototype.compileSlice = function compileSlice(node, frame) { 78 this._emit('('); 79 this._compileExpression(node.start, frame); 80 this._emit('),('); 81 this._compileExpression(node.stop, frame); 82 this._emit('),('); 83 this._compileExpression(node.step, frame); 84 this._emit(')'); 85 }; 86 Parser.prototype.parseAggregate = function parseAggregate() { 87 var _this = this; 88 var origState = getTokensState(this.tokens); 89 // Set back one accounting for opening bracket/parens 90 origState.colno--; 91 origState.index--; 92 try { 93 return orig_Parser_parseAggregate.apply(this); 94 } catch (e) { 95 var errState = getTokensState(this.tokens); 96 var rethrow = function rethrow() { 97 lib._assign(_this.tokens, errState); 98 return e; 99 }; 100 101 // Reset to state before original parseAggregate called 102 lib._assign(this.tokens, origState); 103 this.peeked = false; 104 var tok = this.peekToken(); 105 if (tok.type !== lexer.TOKEN_LEFT_BRACKET) { 106 throw rethrow(); 107 } else { 108 this.nextToken(); 109 } 110 var node = new Slice(tok.lineno, tok.colno); 111 112 // If we don't encounter a colon while parsing, this is not a slice, 113 // so re-raise the original exception. 114 var isSlice = false; 115 for (var i = 0; i <= node.fields.length; i++) { 116 if (this.skip(lexer.TOKEN_RIGHT_BRACKET)) { 117 break; 118 } 119 if (i === node.fields.length) { 120 if (isSlice) { 121 this.fail('parseSlice: too many slice components', tok.lineno, tok.colno); 122 } else { 123 break; 124 } 125 } 126 if (this.skip(lexer.TOKEN_COLON)) { 127 isSlice = true; 128 } else { 129 var field = node.fields[i]; 130 node[field] = this.parseExpression(); 131 isSlice = this.skip(lexer.TOKEN_COLON) || isSlice; 132 } 133 } 134 if (!isSlice) { 135 throw rethrow(); 136 } 137 return new nodes.Array(tok.lineno, tok.colno, [node]); 138 } 139 }; 140 } 141 function sliceLookup(obj, start, stop, step) { 142 obj = obj || []; 143 if (start === null) { 144 start = step < 0 ? obj.length - 1 : 0; 145 } 146 if (stop === null) { 147 stop = step < 0 ? -1 : obj.length; 148 } else if (stop < 0) { 149 stop += obj.length; 150 } 151 if (start < 0) { 152 start += obj.length; 153 } 154 var results = []; 155 for (var i = start;; i += step) { 156 if (i < 0 || i > obj.length) { 157 break; 158 } 159 if (step > 0 && i >= stop) { 160 break; 161 } 162 if (step < 0 && i <= stop) { 163 break; 164 } 165 results.push(runtime.memberLookup(obj, i)); 166 } 167 return results; 168 } 169 function hasOwnProp(obj, key) { 170 return Object.prototype.hasOwnProperty.call(obj, key); 171 } 172 var ARRAY_MEMBERS = { 173 pop: function pop(index) { 174 if (index === undefined) { 175 return this.pop(); 176 } 177 if (index >= this.length || index < 0) { 178 throw new Error('KeyError'); 179 } 180 return this.splice(index, 1); 181 }, 182 append: function append(element) { 183 return this.push(element); 184 }, 185 remove: function remove(element) { 186 for (var i = 0; i < this.length; i++) { 187 if (this[i] === element) { 188 return this.splice(i, 1); 189 } 190 } 191 throw new Error('ValueError'); 192 }, 193 count: function count(element) { 194 var count = 0; 195 for (var i = 0; i < this.length; i++) { 196 if (this[i] === element) { 197 count++; 198 } 199 } 200 return count; 201 }, 202 index: function index(element) { 203 var i; 204 if ((i = this.indexOf(element)) === -1) { 205 throw new Error('ValueError'); 206 } 207 return i; 208 }, 209 find: function find(element) { 210 return this.indexOf(element); 211 }, 212 insert: function insert(index, elem) { 213 return this.splice(index, 0, elem); 214 } 215 }; 216 var OBJECT_MEMBERS = { 217 items: function items() { 218 return lib._entries(this); 219 }, 220 values: function values() { 221 return lib._values(this); 222 }, 223 keys: function keys() { 224 return lib.keys(this); 225 }, 226 get: function get(key, def) { 227 var output = this[key]; 228 if (output === undefined) { 229 output = def; 230 } 231 return output; 232 }, 233 has_key: function has_key(key) { 234 return hasOwnProp(this, key); 235 }, 236 pop: function pop(key, def) { 237 var output = this[key]; 238 if (output === undefined && def !== undefined) { 239 output = def; 240 } else if (output === undefined) { 241 throw new Error('KeyError'); 242 } else { 243 delete this[key]; 244 } 245 return output; 246 }, 247 popitem: function popitem() { 248 var keys = lib.keys(this); 249 if (!keys.length) { 250 throw new Error('KeyError'); 251 } 252 var k = keys[0]; 253 var val = this[k]; 254 delete this[k]; 255 return [k, val]; 256 }, 257 setdefault: function setdefault(key, def) { 258 if (def === void 0) { 259 def = null; 260 } 261 if (!(key in this)) { 262 this[key] = def; 263 } 264 return this[key]; 265 }, 266 update: function update(kwargs) { 267 lib._assign(this, kwargs); 268 return null; // Always returns None 269 } 270 }; 271 272 OBJECT_MEMBERS.iteritems = OBJECT_MEMBERS.items; 273 OBJECT_MEMBERS.itervalues = OBJECT_MEMBERS.values; 274 OBJECT_MEMBERS.iterkeys = OBJECT_MEMBERS.keys; 275 runtime.memberLookup = function memberLookup(obj, val, autoescape) { 276 if (arguments.length === 4) { 277 return sliceLookup.apply(this, arguments); 278 } 279 obj = obj || {}; 280 281 // If the object is an object, return any of the methods that Python would 282 // otherwise provide. 283 if (lib.isArray(obj) && hasOwnProp(ARRAY_MEMBERS, val)) { 284 return ARRAY_MEMBERS[val].bind(obj); 285 } 286 if (lib.isObject(obj) && hasOwnProp(OBJECT_MEMBERS, val)) { 287 return OBJECT_MEMBERS[val].bind(obj); 288 } 289 return orig_memberLookup.apply(this, arguments); 290 }; 291 return uninstall; 292} 293module.exports = installCompat;