@stdlib/array
Version:
Arrays.
164 lines (148 loc) • 4.7 kB
JavaScript
/**
* @license Apache-2.0
*
* Copyright (c) 2024 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
// MODULES //
var trim = require( '@stdlib/string/base/trim' );
var seq2slice = require( '@stdlib/slice/base/seq2slice' );
var str2slice = require( '@stdlib/slice/base/str2slice' );
var startsWith = require( '@stdlib/string/base/starts-with' );
var format = require( '@stdlib/string/format' );
var RE_SUBSEQ = require( './re_subseq.js' );
// FUNCTIONS //
/**
* Tests if an indexing expression is a serialized Slice object.
*
* @private
* @param {string} prop - property name
* @returns {boolean} result
*
* @example
* var out = isSlice( 'Slice(null,null,1)' );
* // returns true
*
* @example
* var out = isSlice( ':' );
* // returns false
*/
function isSlice( prop ) {
return (
prop[ 0 ] === 'S' &&
startsWith( prop, 'Slice(', 0 ) &&
prop[ prop.length-1 ] === ')'
);
}
/**
* Tests if an indexing expression is a subsequence.
*
* @private
* @param {string} prop - property name
* @returns {boolean} result
*
* @example
* var out = isSubsequence( '::-2' );
* // returns true
*
* @example
* var out = isSubsequence( '-2' );
* // returns false
*/
function isSubsequence( prop ) {
// TODO: consider whether to make this check more robust (e.g., should we actually throw if someone tries to access `foo:bar`? If we make this check more exact, how would we distinguish between a non-existent `foo:bar` property and an actual error in the subsequence string?)
return RE_SUBSEQ.test( prop );
}
/**
* Parses a serialized Slice object.
*
* @private
* @param {string} raw - original unprocessed input string
* @param {string} str - serialized Slice object
* @throws {Error} invalid slice operation
* @returns {Slice} Slice object
*
* @example
* var s = parseSlice( ' Slice(null,null,1) ', 'Slice(null,null,1)' );
* // returns <Slice>
*/
function parseSlice( raw, str ) {
var s = str2slice( str );
if ( s === null ) {
throw new Error( format( 'invalid operation. Unsupported slice operation. Value: `%s`.', raw ) );
}
return s;
}
/**
* Parses a subsequence string.
*
* @private
* @param {string} raw - original unprocessed input string
* @param {string} str - subsequence string
* @param {NonNegativeInteger} max - index upper bound
* @param {boolean} strict - boolean indicating whether to enforce strict bounds checking
* @throws {Error} invalid slice operation
* @throws {RangeError} slice exceeds array bounds
* @returns {Slice} Slice object
*
* @example
* var s = parseSubsequence( ' ::-2 ', '::-2', 10, false );
* // returns <Slice>
*/
function parseSubsequence( raw, str, max, strict ) {
var s = seq2slice( str, max, true );
if ( s.code ) {
if ( s.code === 'ERR_SLICE_INVALID_INCREMENT' ) {
throw new Error( format( 'invalid operation. A subsequence increment must be a non-zero integer. Value: `%s`.', raw ) );
}
if ( s.code === 'ERR_SLICE_INVALID_SUBSEQUENCE' ) {
throw new Error( format( 'invalid operation. Unsupported slice operation. Value: `%s`.', raw ) );
}
// NOTE: the following error check must come last due to fall-through when in non-strict mode...
if ( s.code === 'ERR_SLICE_OUT_OF_BOUNDS' ) {
if ( strict ) {
throw new RangeError( format( 'invalid operation. Slice exceeds array bounds.' ) );
}
// Repeat parsing, this time allowing for out-of-bounds slices:
s = seq2slice( str, max, false );
}
}
return s;
}
// MAIN //
/**
* Converts an indexing expression to a Slice object.
*
* @private
* @param {Object} target - target object
* @param {string} property - property name
* @param {boolean} strict - boolean indicating whether to enforce strict bounds checking
* @throws {Error} invalid slice operation
* @throws {RangeError} slice exceeds array bounds
* @returns {(Slice|null)} slice object (or null)
*/
function prop2slice( target, property, strict ) {
var prop = trim( property );
if ( isSlice( prop ) ) {
return parseSlice( property, prop );
}
if ( isSubsequence( prop ) ) {
return parseSubsequence( property, prop, target.length, strict );
}
// Everything else (including undefined/non-existent properties):
return null;
}
// EXPORTS //
module.exports = prop2slice;