Browse Source

Better doc comments. Fixed `index` bug thanks to tests.

gh-109
Oskar Segersvärd 10 years ago
parent
commit
fc9b4a9c92
  1. 32
      src/Scope.js
  2. 25
      test/testScope.js

32
src/Scope.js

@ -26,7 +26,8 @@ function underscorePrefix ( x ) {
return '_' + x; return '_' + x;
} }
// A Scope is a mapping from name to identifiers. // ## Scope
// A Scope is a mapping from string names to `Identifiers`.
export default class Scope { export default class Scope {
constructor () { constructor () {
this.ids = []; this.ids = [];
@ -42,11 +43,15 @@ export default class Scope {
// Deconflict all names within the scope, // Deconflict all names within the scope,
// using the given renaming function. // using the given renaming function.
// If no function is supplied, the name is simply prefixed with '_'. // If no function is supplied, `underscorePrefix` is used.
deconflict ( rename = underscorePrefix ) { deconflict ( rename = underscorePrefix ) {
const names = this.used; const names = this.used;
this.ids.filter( isntReference ).forEach( id => { this.ids.filter( isntReference ).forEach( id => {
if ( typeof id === 'string' ) {
throw new Error( `Required name ${id} undefined!` );
}
let name = id.name; let name = id.name;
while ( name in names && names[ name ] !== id ) { while ( name in names && names[ name ] !== id ) {
@ -72,20 +77,19 @@ export default class Scope {
return name in this.names; return name in this.names;
} }
// !! private, don't use !! // *private, don't use*
// //
// Lookup the `ids` index of `name`. // Return `name`'s index in the `ids` array if it exists,
// otherwise returns the index to a new placeholder slot.
index ( name ) { index ( name ) {
if ( !( name in this.names ) ) { if ( !( name in this.names ) ) {
// The `undefined` value of this push is a placeholder return this.names[ name ] = this.ids.push( `"${name}"` ) - 1;
// that should be overwritten by the caller.
return this.names[ name ] = this.ids.push();
} }
return this.names[ name ]; return this.names[ name ];
} }
// Returns a list of [ localName, identifier ] tuples. // Returns a list of `[ name, identifier ]` tuples.
localIds () { localIds () {
return keys( this.names ).map( name => [ name, this.lookup( name ) ] ); return keys( this.names ).map( name => [ name, this.lookup( name ) ] );
} }
@ -103,21 +107,17 @@ export default class Scope {
// Get a reference to the identifier `name` in this scope. // Get a reference to the identifier `name` in this scope.
reference ( name ) { reference ( name ) {
if ( !this.defines( name ) ) {
this.define( name );
}
return new Reference( this, this.index( name ) ); return new Reference( this, this.index( name ) );
} }
// Return the names currently in use in the scope. // Return the used names of the scope.
// Names aren't considered used until they're deconflicted. // Names aren't considered used unless they're deconflicted.
usedNames () { usedNames () {
return keys( this.used ).sort(); return keys( this.used ).sort();
} }
// Create and return a virtual scope, bound to // Create and return a virtual `Scope` instance, bound to
// the actual scope of this Scope. // the actual scope of `this`.
virtual () { virtual () {
const scope = new Scope(); const scope = new Scope();
scope.ids = this.ids; scope.ids = this.ids;

25
test/testScope.js

@ -71,4 +71,29 @@ describe( 'Scope', function () {
assert.deepEqual( real.usedNames(), 'abcdefghijklmnopqrstuvwxyz'.split('') ); assert.deepEqual( real.usedNames(), 'abcdefghijklmnopqrstuvwxyz'.split('') );
}); });
}); });
it( 'dedupes-external-imports', function () {
var real = new Scope();
var external = real.virtual(),
locals = real.virtual(),
exports = real.virtual();
external.define( 'Component' );
locals.bind( 'Comp', external.reference( 'Component' ) );
exports.bind( 'default', locals.reference( 'Foo' ) );
try {
real.deconflict();
assert.ok( false, 'Scope.deconflict should throw with "Foo" undefined' );
} catch ( ignore ) {
// as expected
}
locals.define( 'Foo' );
real.deconflict();
});
}); });

Loading…
Cancel
Save