Added utility methods for objects
Before there used to be some internal utility methods for arrays and objects, but as the code base moved to ES5+ use case only, these were removed in favour of native alternatives, but most of these were only beneficial for arrays. This commit add's common utility methods for objects, and also exposes these as they can be used by plugin developer's on the PEG.js AST.master
parent
02486cef7f
commit
7cdfc03e9f
@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
// type Pass = ( ast: {}, options: {} ) => void;
|
||||
// type StageMap = { [string]: { [string]: Pass } };
|
||||
// type PassMap = { [string]: Pass[] };
|
||||
//
|
||||
// The PEG.js compiler runs each `Pass` on the `PassMap` (it's 2nd argument),
|
||||
// but the compiler api exposes a `StageMap` so that it is easier for plugin
|
||||
// developer's to access the built-in passes.
|
||||
//
|
||||
// This file exposes a method that will take a `StageMap`, and return a
|
||||
// `PassMap` that can then be passed to the compiler.
|
||||
|
||||
const objects = require( "./objects" );
|
||||
|
||||
function convertStage( passes ) {
|
||||
|
||||
return Array.isArray( passes )
|
||||
? passes
|
||||
: objects.values( passes );
|
||||
|
||||
}
|
||||
|
||||
function convertPasses( stages ) {
|
||||
|
||||
return objects.map( stages, convertStage );
|
||||
|
||||
}
|
||||
|
||||
module.exports = convertPasses;
|
@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
const objects = require( "./objects" );
|
||||
|
||||
exports.noop = function noop() { };
|
||||
|
||||
exports.convertPasses = require( "./convert-passes" );
|
||||
|
||||
objects.extend( exports, objects );
|
@ -0,0 +1,114 @@
|
||||
"use strict";
|
||||
|
||||
const __hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
const __slice = Array.prototype.slice;
|
||||
|
||||
const objects = {
|
||||
|
||||
// Produce's a shallow clone of the given object.
|
||||
clone( source ) {
|
||||
|
||||
const target = {};
|
||||
|
||||
for ( const key in source ) {
|
||||
|
||||
if ( ! __hasOwnProperty.call( source, key ) ) continue;
|
||||
target[ key ] = source[ key ];
|
||||
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
},
|
||||
|
||||
// Will loop through an object's properties calling the given function.
|
||||
//
|
||||
// NOTE:
|
||||
// This method is just a simplification of:
|
||||
//
|
||||
// Object.keys( object ).forEach( key => value = object[ key ] )`
|
||||
//
|
||||
// It is not meant to be compatible with `Array#forEach`.
|
||||
each( object, iterator ) {
|
||||
|
||||
for ( const key in object ) {
|
||||
|
||||
if ( ! __hasOwnProperty.call( object, key ) ) continue;
|
||||
iterator( object[ key ], key );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// This method add's properties from the source object to the target object,
|
||||
// but only if they don't already exist. It is more similar to how a native
|
||||
// class is extened then the native `Object.assign`.
|
||||
extend( target, source ) {
|
||||
|
||||
for ( const key in source ) {
|
||||
|
||||
if ( ! __hasOwnProperty.call( source, key ) ) continue;
|
||||
if ( __hasOwnProperty.call( target, key ) ) continue;
|
||||
|
||||
target[ key ] = source[ key ];
|
||||
|
||||
}
|
||||
return target;
|
||||
|
||||
},
|
||||
|
||||
// Is similar to `Array#map`, but, just like `each`, it is not compatible,
|
||||
// especially because it returns an object rather then an array.
|
||||
map( object, transformer ) {
|
||||
|
||||
const target = {};
|
||||
|
||||
for ( const key in object ) {
|
||||
|
||||
if ( ! __hasOwnProperty.call( object, key ) ) continue;
|
||||
target[ key ] = transformer( object[ key ], key );
|
||||
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
},
|
||||
|
||||
// This return's an array like `Array#map` does, but the transformer method
|
||||
// is optional, so at the same time behave's like ES2015's `Object.values`.
|
||||
values( object, transformer ) {
|
||||
|
||||
const target = [];
|
||||
let index = -1;
|
||||
let key, value;
|
||||
|
||||
for ( key in object ) {
|
||||
|
||||
if ( ! __hasOwnProperty.call( object, key ) ) continue;
|
||||
value = object[ key ];
|
||||
|
||||
target[ ++index ] = transformer
|
||||
? transformer( value, key )
|
||||
: value;
|
||||
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
},
|
||||
|
||||
// Will return a function that can be used to visit a specific property
|
||||
// no matter what object is passed to it.
|
||||
createVisitor( property, visit ) {
|
||||
|
||||
return function visitProperty( object ) {
|
||||
|
||||
visit( object[ property ], __slice.call( arguments, 1 ) );
|
||||
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = objects;
|
@ -0,0 +1,199 @@
|
||||
"use strict";
|
||||
|
||||
const chai = require( "chai" );
|
||||
const util = require( "pegjs-dev" ).util;
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe( "PEG.js Utility API", function () {
|
||||
|
||||
describe( "util.convertPasses", function () {
|
||||
|
||||
const passes = {
|
||||
stage1: {
|
||||
pass1() { },
|
||||
pass2() { },
|
||||
pass3() { }
|
||||
},
|
||||
stage2: {
|
||||
pass1() { },
|
||||
pass2() { }
|
||||
},
|
||||
stage3: {
|
||||
pass1() { }
|
||||
}
|
||||
};
|
||||
|
||||
function expectPasses( result ) {
|
||||
|
||||
expect( result ).to.be.an( "object" );
|
||||
|
||||
expect( result.stage1 )
|
||||
.to.be.an( "array" )
|
||||
.and.to.have.a.lengthOf( 3 );
|
||||
|
||||
expect( result.stage2 )
|
||||
.to.be.an( "array" )
|
||||
.and.to.have.a.lengthOf( 2 );
|
||||
|
||||
expect( result.stage3 )
|
||||
.to.be.an( "array" )
|
||||
.and.to.have.a.lengthOf( 1 );
|
||||
|
||||
}
|
||||
|
||||
it( "converts a map of stages containing a map of passes", function () {
|
||||
|
||||
expectPasses( util.convertPasses( passes ) );
|
||||
|
||||
} );
|
||||
|
||||
it( "converts a map of stages containing a list of passes", function () {
|
||||
|
||||
expectPasses( util.convertPasses( {
|
||||
stage1: [
|
||||
passes.stage1.pass1,
|
||||
passes.stage1.pass2,
|
||||
passes.stage1.pass3
|
||||
],
|
||||
stage2: passes.stage2,
|
||||
stage3: [
|
||||
passes.stage3.pass1
|
||||
]
|
||||
} ) );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
describe( "util.clone", function () {
|
||||
|
||||
const meta = { name: "pegjs", version: 0.11, util: util };
|
||||
|
||||
it( "shallow clones an object", function () {
|
||||
|
||||
expect( util.clone( meta ) )
|
||||
.to.be.an( "object" )
|
||||
.that.has.own.includes( meta );
|
||||
|
||||
} );
|
||||
|
||||
it( "cloned properties refrence same value", function () {
|
||||
|
||||
expect( util.clone( meta ) )
|
||||
.to.haveOwnProperty( "util" )
|
||||
.that.is.a( "object" )
|
||||
.which.equals( util );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
describe( "util.each", function () {
|
||||
|
||||
it( "should iterate over an objects properties", function () {
|
||||
|
||||
const size = Object.keys( util ).length;
|
||||
const entries = [];
|
||||
|
||||
util.each( util, ( value, key ) => {
|
||||
|
||||
entries.push( { key, value } );
|
||||
|
||||
} );
|
||||
|
||||
expect( entries.length ).to.equal( size );
|
||||
|
||||
entries.forEach( entry => {
|
||||
|
||||
expect( util )
|
||||
.to.have.ownProperty( entry.key )
|
||||
.which.equals( entry.value );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
describe( "util.values", function () {
|
||||
|
||||
const map = { a: 1, b: 2, c: 3 };
|
||||
|
||||
it( "can extract values like Object.values", function () {
|
||||
|
||||
expect( util.values( map ) )
|
||||
.to.be.an( "array" )
|
||||
.with.a.lengthOf( 3 )
|
||||
.and.includes.members( [ 1, 2, 3 ] );
|
||||
|
||||
} );
|
||||
|
||||
it( "can take a transformer, like Array#map", function () {
|
||||
|
||||
expect( util.values( map, n => String( n ) ) )
|
||||
.that.includes.members( [ "1", "2", "3" ] );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
describe( "util.extend", function () {
|
||||
|
||||
const source = { d: 4, e: 5, f: 6, g: 7, h: 8 };
|
||||
|
||||
it( "extend an empty object", function () {
|
||||
|
||||
const target = {};
|
||||
|
||||
expect( util.extend( target, source ) )
|
||||
.to.be.an( "object" )
|
||||
.that.includes.keys( Object.keys( source ) );
|
||||
|
||||
expect( util.values( target ) )
|
||||
.to.include.members( [ 4, 5, 6, 7, 8 ] )
|
||||
.and.have.a.lengthOf( 5 );
|
||||
|
||||
} );
|
||||
|
||||
it( "extend an object", function () {
|
||||
|
||||
const target = util.extend( {}, source );
|
||||
const utils = Object.keys( util );
|
||||
|
||||
expect( util.extend( target, util ) )
|
||||
.to.include.keys( utils );
|
||||
|
||||
expect( util.values( target ) )
|
||||
.to.have.a.lengthOf( 5 + utils.length );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
describe( "util.map", function () {
|
||||
|
||||
const object = { a: 1, b: 2, c: 3, d: 4 };
|
||||
const result = util.map( object, String );
|
||||
|
||||
it( "returns an object, and not an array, unlike Array#map", function () {
|
||||
|
||||
expect( result )
|
||||
.to.be.an( "object" )
|
||||
.that.includes.keys( Object.keys( object ) );
|
||||
|
||||
} );
|
||||
|
||||
it( "applies a transformation on each properties value", function () {
|
||||
|
||||
util.each( result, property => {
|
||||
|
||||
expect( property ).to.be.a( "string" );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
Loading…
Reference in New Issue