time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

main.js (6043B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2022 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 'use strict';
     20 
     21 // MODULES //
     22 
     23 var formatInteger = require( './format_integer.js' );
     24 var isString = require( './is_string.js' );
     25 var formatDouble = require( './format_double.js' );
     26 var spacePad = require( './space_pad.js' );
     27 var zeroPad = require( './zero_pad.js' );
     28 
     29 
     30 // VARIABLES //
     31 
     32 var fromCharCode = String.fromCharCode;
     33 var isnan = isNaN; // NOTE: We use the global `isNaN` function here instead of `@stdlib/math/base/assert/is-nan` to avoid circular dependencies.
     34 var isArray = Array.isArray; // NOTE: We use the global `Array.isArray` function here instead of `@stdlib/assert/is-array` to avoid circular dependencies.
     35 
     36 
     37 // FUNCTIONS //
     38 
     39 /**
     40 * Initializes token object with properties of supplied format identifier object or default values if not present.
     41 *
     42 * @private
     43 * @param {Object} token - format identifier object
     44 * @returns {Object} token object
     45 */
     46 function initialize( token ) {
     47 	var out = {};
     48 	out.specifier = token.specifier;
     49 	out.precision = ( token.precision === void 0 ) ? 1 : token.precision;
     50 	out.width = token.width;
     51 	out.flags = token.flags || '';
     52 	out.mapping = token.mapping;
     53 	return out;
     54 }
     55 
     56 
     57 // MAIN //
     58 
     59 /**
     60 * Generates string from a token array by interpolating values.
     61 *
     62 * @param {Array} tokens - string parts and format identifier objects
     63 * @param {Array} ...args - variable values
     64 * @throws {TypeError} first argument must be an array
     65 * @throws {Error} invalid flags
     66 * @returns {string} formatted string
     67 *
     68 * @example
     69 * var tokens = [ 'beep ', { 'specifier': 's' } ];
     70 * var out = formatInterpolate( tokens, 'boop' );
     71 * // returns 'beep boop'
     72 */
     73 function formatInterpolate( tokens ) {
     74 	var hasPeriod;
     75 	var flags;
     76 	var token;
     77 	var flag;
     78 	var num;
     79 	var out;
     80 	var pos;
     81 	var i;
     82 	var j;
     83 
     84 	if ( !isArray( tokens ) ) {
     85 		throw new TypeError( 'invalid argument. First argument must be an array. Value: `' + tokens + '`.' );
     86 	}
     87 	out = '';
     88 	pos = 1;
     89 	for ( i = 0; i < tokens.length; i++ ) {
     90 		token = tokens[ i ];
     91 		if ( isString( token ) ) {
     92 			out += token;
     93 		} else {
     94 			hasPeriod = token.precision !== void 0;
     95 			token = initialize( token );
     96 			if ( !token.specifier ) {
     97 				throw new TypeError( 'invalid argument. Token is missing `specifier` property. Index: `'+ i +'`. Value: `' + token + '`.' );
     98 			}
     99 			if ( token.mapping ) {
    100 				pos = token.mapping;
    101 			}
    102 			flags = token.flags;
    103 			for ( j = 0; j < flags.length; j++ ) {
    104 				flag = flags.charAt( j );
    105 				switch ( flag ) {
    106 				case ' ':
    107 					token.sign = ' ';
    108 					break;
    109 				case '+':
    110 					token.sign = '+';
    111 					break;
    112 				case '-':
    113 					token.padRight = true;
    114 					token.padZeros = false;
    115 					break;
    116 				case '0':
    117 					token.padZeros = flags.indexOf( '-' ) < 0; // NOTE: We use built-in `Array.prototype.indexOf` here instead of `@stdlib/assert/contains` in order to avoid circular dependencies.
    118 					break;
    119 				case '#':
    120 					token.alternate = true;
    121 					break;
    122 				default:
    123 					throw new Error( 'invalid flag: ' + flag );
    124 				}
    125 			}
    126 			if ( token.width === '*' ) {
    127 				token.width = parseInt( arguments[ pos ], 10 );
    128 				pos += 1;
    129 				if ( isnan( token.width ) ) {
    130 					throw new TypeError( 'the argument for * width at position ' + pos + ' is not a number. Value: `' + token.width + '`.' );
    131 				}
    132 				if ( token.width < 0 ) {
    133 					token.padRight = true;
    134 					token.width = -token.width;
    135 				}
    136 			}
    137 			if ( hasPeriod ) {
    138 				if ( token.precision === '*' ) {
    139 					token.precision = parseInt( arguments[ pos ], 10 );
    140 					pos += 1;
    141 					if ( isnan( token.precision ) ) {
    142 						throw new TypeError( 'the argument for * precision at position ' + pos + ' is not a number. Value: `' + token.precision + '`.' );
    143 					}
    144 					if ( token.precision < 0 ) {
    145 						token.precision = 1;
    146 						hasPeriod = false;
    147 					}
    148 				}
    149 			}
    150 			token.arg = arguments[ pos ];
    151 			switch ( token.specifier ) {
    152 			case 'b':
    153 			case 'o':
    154 			case 'x':
    155 			case 'X':
    156 			case 'd':
    157 			case 'i':
    158 			case 'u':
    159 				// Case: %b (binary), %o (octal), %x, %X (hexadecimal), %d, %i (decimal), %u (unsigned decimal)
    160 				if ( hasPeriod ) {
    161 					token.padZeros = false;
    162 				}
    163 				token.arg = formatInteger( token );
    164 				break;
    165 			case 's':
    166 				// Case: %s (string)
    167 				token.maxWidth = ( hasPeriod ) ? token.precision : -1;
    168 				break;
    169 			case 'c':
    170 				// Case: %c (character)
    171 				if ( !isnan( token.arg ) ) {
    172 					num = parseInt( token.arg, 10 );
    173 					if ( num < 0 || num > 127 ) {
    174 						throw new Error( 'invalid character code. Value: ' + token.arg );
    175 					}
    176 					token.arg = ( isnan( num ) ) ?
    177 						String( token.arg ) :
    178 						fromCharCode( num );
    179 				}
    180 				break;
    181 			case 'e':
    182 			case 'E':
    183 			case 'f':
    184 			case 'F':
    185 			case 'g':
    186 			case 'G':
    187 				// Case: %e, %E (scientific notation), %f, %F (decimal floating point), %g, %G (uses the shorter of %e/E or %f/F)
    188 				if ( !hasPeriod ) {
    189 					token.precision = 6;
    190 				}
    191 				token.arg = formatDouble( token );
    192 				break;
    193 			default:
    194 				throw new Error( 'invalid specifier: ' + token.specifier );
    195 			}
    196 			// Fit argument into field width...
    197 			if ( token.maxWidth >= 0 && token.arg.length > token.maxWidth ) {
    198 				token.arg = token.arg.substring( 0, token.maxWidth );
    199 			}
    200 			if ( token.padZeros ) {
    201 				token.arg = zeroPad( token.arg, token.width || token.precision, token.padRight ); // eslint-disable-line max-len
    202 			} else if ( token.width ) {
    203 				token.arg = spacePad( token.arg, token.width, token.padRight );
    204 			}
    205 			out += token.arg || '';
    206 			pos += 1;
    207 		}
    208 	}
    209 	return out;
    210 }
    211 
    212 
    213 // EXPORTS //
    214 
    215 module.exports = formatInterpolate;