2020-08-26 01:57:08 +02:00
'use strict' ;
2021-02-26 04:58:33 +01:00
var GetIntrinsic = require ( 'get-intrinsic' ) ;
2020-08-26 01:57:08 +02:00
var $TypeError = GetIntrinsic ( '%TypeError%' ) ;
var $parseInt = GetIntrinsic ( '%parseInt%' ) ;
var inspect = require ( 'object-inspect' ) ;
2022-11-10 11:43:16 +01:00
var regexTester = require ( 'safe-regex-test' ) ;
2021-02-26 04:58:33 +01:00
var callBound = require ( 'call-bind/callBound' ) ;
2020-08-26 01:57:08 +02:00
var every = require ( '../helpers/every' ) ;
var isDigit = regexTester ( /^[0-9]$/ ) ;
var $charAt = callBound ( 'String.prototype.charAt' ) ;
var $strSlice = callBound ( 'String.prototype.slice' ) ;
var IsArray = require ( './IsArray' ) ;
var IsInteger = require ( './IsInteger' ) ;
var Type = require ( './Type' ) ;
var canDistinguishSparseFromUndefined = 0 in [ undefined ] ; // IE 6 - 8 have a bug where this returns false
var isStringOrHole = function ( capture , index , arr ) {
return Type ( capture ) === 'String' || ( canDistinguishSparseFromUndefined ? ! ( index in arr ) : Type ( capture ) === 'Undefined' ) ;
} ;
2021-02-26 04:58:33 +01:00
// https://ecma-international.org/ecma-262/6.0/#sec-getsubstitution
2020-08-26 01:57:08 +02:00
// eslint-disable-next-line max-statements, max-params, max-lines-per-function
module . exports = function GetSubstitution ( matched , str , position , captures , replacement ) {
if ( Type ( matched ) !== 'String' ) {
throw new $TypeError ( 'Assertion failed: `matched` must be a String' ) ;
}
var matchLength = matched . length ;
if ( Type ( str ) !== 'String' ) {
throw new $TypeError ( 'Assertion failed: `str` must be a String' ) ;
}
var stringLength = str . length ;
if ( ! IsInteger ( position ) || position < 0 || position > stringLength ) {
throw new $TypeError ( 'Assertion failed: `position` must be a nonnegative integer, and less than or equal to the length of `string`, got ' + inspect ( position ) ) ;
}
if ( ! IsArray ( captures ) || ! every ( captures , isStringOrHole ) ) {
throw new $TypeError ( 'Assertion failed: `captures` must be a List of Strings, got ' + inspect ( captures ) ) ;
}
if ( Type ( replacement ) !== 'String' ) {
throw new $TypeError ( 'Assertion failed: `replacement` must be a String' ) ;
}
var tailPos = position + matchLength ;
var m = captures . length ;
var result = '' ;
for ( var i = 0 ; i < replacement . length ; i += 1 ) {
// if this is a $, and it's not the end of the replacement
var current = $charAt ( replacement , i ) ;
var isLast = ( i + 1 ) >= replacement . length ;
var nextIsLast = ( i + 2 ) >= replacement . length ;
if ( current === '$' && ! isLast ) {
var next = $charAt ( replacement , i + 1 ) ;
if ( next === '$' ) {
result += '$' ;
i += 1 ;
} else if ( next === '&' ) {
result += matched ;
i += 1 ;
} else if ( next === '`' ) {
result += position === 0 ? '' : $strSlice ( str , 0 , position - 1 ) ;
i += 1 ;
} else if ( next === "'" ) {
result += tailPos >= stringLength ? '' : $strSlice ( str , tailPos ) ;
i += 1 ;
} else {
var nextNext = nextIsLast ? null : $charAt ( replacement , i + 2 ) ;
if ( isDigit ( next ) && next !== '0' && ( nextIsLast || ! isDigit ( nextNext ) ) ) {
// $1 through $9, and not followed by a digit
var n = $parseInt ( next , 10 ) ;
// if (n > m, impl-defined)
2021-02-26 04:58:33 +01:00
result += n <= m && Type ( captures [ n - 1 ] ) === 'Undefined' ? '' : captures [ n - 1 ] ;
2020-08-26 01:57:08 +02:00
i += 1 ;
} else if ( isDigit ( next ) && ( nextIsLast || isDigit ( nextNext ) ) ) {
// $00 through $99
var nn = next + nextNext ;
var nnI = $parseInt ( nn , 10 ) - 1 ;
// if nn === '00' or nn > m, impl-defined
2021-02-26 04:58:33 +01:00
result += nn <= m && Type ( captures [ nnI ] ) === 'Undefined' ? '' : captures [ nnI ] ;
2020-08-26 01:57:08 +02:00
i += 2 ;
} else {
result += '$' ;
}
}
} else {
// the final $, or else not a $
result += $charAt ( replacement , i ) ;
}
}
return result ;
} ;