main.js (8327B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 /* eslint-disable no-restricted-syntax, no-invalid-this */ 20 21 'use strict'; 22 23 // MODULES // 24 25 var setReadOnly = require( './../../define-nonenumerable-read-only-property' ); 26 var setReadOnlyAccessor = require( './../../define-nonenumerable-read-only-accessor' ); 27 var iteratorSymbol = require( '@stdlib/symbol/iterator' ); 28 29 30 // MAIN // 31 32 /** 33 * Stack constructor. 34 * 35 * @constructor 36 * @returns {Stack} stack instance 37 * 38 * @example 39 * var s = new Stack(); 40 * 41 * // Add values to the stack: 42 * s.push( 'foo' ).push( 'bar' ); 43 * 44 * // Remove the top value: 45 * var v = s.pop(); 46 * // returns 'bar' 47 * 48 * // Add a new value to the stack: 49 * s.push( 'beep' ); 50 * 51 * // Remove the top value: 52 * v = s.pop(); 53 * // returns 'beep' 54 */ 55 function Stack() { 56 if ( !(this instanceof Stack) ) { 57 return new Stack(); 58 } 59 // Use a dynamic array as insertion and deletion is O(1) amortized: 60 this._buffer = []; 61 return this; 62 } 63 64 /** 65 * Clears the stack. 66 * 67 * @name clear 68 * @memberof Stack.prototype 69 * @type {Function} 70 * @returns {Stack} stack instance 71 * 72 * @example 73 * var s = new Stack(); 74 * 75 * // Add values to the stack: 76 * s.push( 'foo' ).push( 'bar' ); 77 * 78 * // Peek at the top value: 79 * var v = s.first(); 80 * // returns 'bar' 81 * 82 * // Examine the stack length: 83 * var len = s.length; 84 * // returns 2 85 * 86 * // Clear all stack items: 87 * s.clear(); 88 * 89 * // Peek at the top value: 90 * v = s.first(); 91 * // returns undefined 92 * 93 * // Examine the stack length: 94 * len = s.length; 95 * // returns 0 96 */ 97 setReadOnly( Stack.prototype, 'clear', function clear() { 98 this._buffer.length = 0; 99 return this; 100 }); 101 102 /** 103 * Returns the top value (i.e., the value which is "first-out"). 104 * 105 * @name first 106 * @memberof Stack.prototype 107 * @type {Function} 108 * @returns {(*|void)} top value 109 * 110 * @example 111 * var s = new Stack(); 112 * 113 * // Add values to the stack: 114 * s.push( 'foo' ).push( 'bar' ); 115 * 116 * // Peek at the top value: 117 * var v = s.first(); 118 * // returns 'bar' 119 */ 120 setReadOnly( Stack.prototype, 'first', function first() { 121 if ( this._buffer.length ) { 122 return this._buffer[ this._buffer.length-1 ]; 123 } 124 }); 125 126 /** 127 * Returns an iterator for iterating over a stack. 128 * 129 * ## Notes 130 * 131 * - In order to prevent confusion arising from stack mutation during iteration, a returned iterator **always** iterates over a stack "snapshot", which is defined as the list of stack elements at the time of this method's invocation. 132 * 133 * @name iterator 134 * @memberof Stack.prototype 135 * @type {Function} 136 * @returns {Iterator} iterator 137 * 138 * @example 139 * var s = new Stack(); 140 * 141 * // Add values to the stack: 142 * s.push( 'foo' ).push( 'bar' ); 143 * 144 * // Create an iterator: 145 * var it = s.iterator(); 146 * 147 * // Iterate over the stack... 148 * var v = it.next().value; 149 * // returns 'bar' 150 * 151 * v = it.next().value; 152 * // returns 'foo' 153 * 154 * var bool = it.next().done; 155 * // returns true 156 */ 157 setReadOnly( Stack.prototype, 'iterator', function iterator() { 158 var values; 159 var iter; 160 var self; 161 var FLG; 162 var i; 163 164 self = this; 165 166 // Initialize the iteration index: 167 i = this._buffer.length; 168 169 // Create a copy of stack values (necessary in order to "snapshot" the stack; otherwise, values could come and go between calls to `next`): 170 values = this._buffer.slice(); 171 172 // Create an iterator protocol-compliant object: 173 iter = {}; 174 setReadOnly( iter, 'next', next ); 175 setReadOnly( iter, 'return', end ); 176 if ( iteratorSymbol ) { 177 setReadOnly( iter, iteratorSymbol, factory ); 178 } 179 return iter; 180 181 /** 182 * Returns an iterator protocol-compliant object containing the next iterated value. 183 * 184 * @private 185 * @returns {Object} iterator protocol-compliant object 186 */ 187 function next() { 188 i -= 1; 189 if ( FLG || i < 0 ) { 190 return { 191 'done': true 192 }; 193 } 194 return { 195 'value': values[ i ], 196 'done': false 197 }; 198 } 199 200 /** 201 * Finishes an iterator. 202 * 203 * @private 204 * @param {*} [value] - value to return 205 * @returns {Object} iterator protocol-compliant object 206 */ 207 function end( value ) { 208 FLG = true; 209 if ( arguments.length ) { 210 return { 211 'value': value, 212 'done': true 213 }; 214 } 215 return { 216 'done': true 217 }; 218 } 219 220 /** 221 * Returns a new iterator. 222 * 223 * @private 224 * @returns {Iterator} iterator 225 */ 226 function factory() { 227 return self.iterator(); 228 } 229 }); 230 231 /** 232 * Returns the bottom stack value (i.e., the value which is currently "last-out"). 233 * 234 * @name last 235 * @memberof Stack.prototype 236 * @type {Function} 237 * @returns {(*|void)} bottom stack value 238 * 239 * @example 240 * var s = new Stack(); 241 * 242 * // Add values to the stack: 243 * s.push( 'foo' ).push( 'bar' ); 244 * 245 * // Peek at the bottom value: 246 * var v = s.last(); 247 * // returns 'foo' 248 */ 249 setReadOnly( Stack.prototype, 'last', function last() { 250 if ( this._buffer.length ) { 251 return this._buffer[ 0 ]; 252 } 253 }); 254 255 /** 256 * Stack length. 257 * 258 * @name length 259 * @memberof Stack.prototype 260 * @type {NonNegativeInteger} 261 * 262 * @example 263 * var s = new Stack(); 264 * 265 * // Examine the initial stack length: 266 * var len = s.length; 267 * // returns 0 268 * 269 * // Add values to the stack: 270 * s.push( 'foo' ).push( 'bar' ); 271 * 272 * // Retrieve the current stack length: 273 * len = s.length; 274 * // returns 2 275 */ 276 setReadOnlyAccessor( Stack.prototype, 'length', function get() { 277 return this._buffer.length; 278 }); 279 280 /** 281 * Removes a value from the stack. 282 * 283 * @name pop 284 * @memberof Stack.prototype 285 * @type {Function} 286 * @returns {(*|void)} removed value 287 * 288 * @example 289 * var s = new Stack(); 290 * 291 * // Add values to the stack: 292 * s.push( 'foo' ).push( 'bar' ); 293 * 294 * // Remove the top value: 295 * var v = s.pop(); 296 * // returns 'bar' 297 * 298 * // Add a new value to the stack: 299 * s.push( 'beep' ); 300 * 301 * // Remove the top value: 302 * v = s.pop(); 303 * // returns 'beep' 304 */ 305 setReadOnly( Stack.prototype, 'pop', function pop() { 306 if ( this._buffer.length ) { 307 return this._buffer.pop(); 308 } 309 }); 310 311 /** 312 * Adds a value to the stack. 313 * 314 * @name push 315 * @memberof Stack.prototype 316 * @type {Function} 317 * @returns {Stack} stack instance 318 * 319 * @example 320 * var s = new Stack(); 321 * 322 * // Add values to the stack: 323 * s.push( 'foo' ).push( 'bar' ); 324 * 325 * // Remove the top value: 326 * var v = s.pop(); 327 * // returns 'bar' 328 * 329 * // Add a new value to the stack: 330 * s.push( 'beep' ); 331 * 332 * // Remove the top value: 333 * v = s.pop(); 334 * // returns 'beep' 335 */ 336 setReadOnly( Stack.prototype, 'push', function push( value ) { 337 this._buffer.push( value ); 338 return this; 339 }); 340 341 /** 342 * Returns an array of stack values. 343 * 344 * ## Notes 345 * 346 * - Why reverse insertion order? Pros and cons to either order, but reverse insertion order mirrors iterator order. For example, we can use the ES6/ES2015+ spread operator along with the iterator to return stack values. 347 * 348 * ```text 349 * arr = [...s.iterator()] 350 * ``` 351 * 352 * One might (as is the opinion here) consider parity of array serialization and iterator order to be a reasonable design goal. 353 * 354 * @name toArray 355 * @memberof Stack.prototype 356 * @type {Function} 357 * @returns {Array} stack values 358 * 359 * @example 360 * var s = new Stack(); 361 * 362 * // Add values to the stack: 363 * s.push( 'foo' ).push( 'bar' ); 364 * 365 * // Get an array of stack values: 366 * var vals = s.toArray(); 367 * // returns [ 'bar', 'foo' ] 368 */ 369 setReadOnly( Stack.prototype, 'toArray', function toArray() { 370 var out; 371 var i; 372 out = []; 373 for ( i = this._buffer.length-1; i >= 0; i-- ) { 374 out.push( this._buffer[ i ] ); 375 } 376 return out; 377 }); 378 379 /** 380 * Serializes a stack as JSON. 381 * 382 * ## Notes 383 * 384 * - `JSON.stringify()` implicitly calls this method when stringifying a `Stack` instance. 385 * 386 * @name toJSON 387 * @memberof Stack.prototype 388 * @type {Function} 389 * @returns {Object} serialized stack 390 * 391 * @example 392 * var s = new Stack(); 393 * 394 * // Add values to the stack: 395 * s.push( 'foo' ).push( 'bar' ); 396 * 397 * // Serialize to JSON: 398 * var o = s.toJSON(); 399 * // returns { 'type': 'stack', 'data': [ 'bar', 'foo' ] } 400 */ 401 setReadOnly( Stack.prototype, 'toJSON', function toJSON() { 402 var out = {}; 403 out.type = 'stack'; 404 out.data = this.toArray(); 405 return out; 406 }); 407 408 409 // EXPORTS // 410 411 module.exports = Stack;