Browse Source

Added tests for Scope, stricter .eslintrc, and contributors.

gh-109
Oskar Segersvärd 9 years ago
parent
commit
9f6fdd2e13
  1. 1
      .babelrc
  2. 6
      .eslintrc
  3. 4
      package.json
  4. 78
      src/Scope.js
  5. 74
      test/testScope.js

1
.babelrc

@ -5,6 +5,7 @@
"es6.classes",
"es6.constants",
"es6.destructuring",
"es6.modules",
"es6.parameters",
"es6.properties.shorthand",
"es6.spread",

6
.eslintrc

@ -1,15 +1,19 @@
{
"rules": {
"indent": [ 2, "tab", { "SwitchCase": 1}],
"indent": [ 2, "tab", { "SwitchCase": 1 } ],
"quotes": [ 2, "single" ],
"linebreak-style": [ 2, "unix" ],
"semi": [ 2, "always" ],
"space-after-keywords": [ 2, "always" ],
"space-before-blocks": [ 2, "always" ],
"space-before-function-paren": [ 2, "always" ],
"no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
"no-cond-assign": [ 0 ]
},
"env": {
"es6": true,
"browser": true,
"mocha": true,
"node": true
},
"extends": "eslint:recommended",

4
package.json

@ -26,12 +26,16 @@
"optimizer"
],
"author": "Rich Harris",
"contributors": [
"Oskar Segersvärd <victorystick@gmail.com>"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/rich-harris/rollup/issues"
},
"homepage": "https://github.com/rich-harris/rollup",
"devDependencies": {
"babel": "^5.8.21",
"babel-core": "^5.5.8",
"console-group": "^0.1.2",
"eslint": "^1.1.0",

78
src/Scope.js

@ -1,43 +1,54 @@
import { blank } from './utils/object';
import { blank, keys } from './utils/object';
class Identifier {
constructor ( name ) {
this.originalName = this.name = name;
}
// A minimal `Identifier` implementation. Anything that has an `originalName`,
// and a mutable `name` property can be used as an `Identifier`.
function Identifier ( name ) {
this.originalName = this.name = name;
}
class Reference {
constructor ( scope, index ) {
this.scope = scope;
this.index = index;
}
// A reference to an `Identifier`.
function Reference ( scope, index ) {
this.scope = scope;
this.index = index;
}
deref () {
return this.scope.ids[ this.index ];
}
// Dereferences a `Reference`.
function dereference ( ref ) {
return ref.scope.ids[ ref.index ];
}
function isntReference ( id ) {
return !( id instanceof Reference );
}
// Prefix the argument with _.
// Prefix the argument with '_'.
function underscorePrefix ( x ) {
return '_' + x;
}
// A Scope is a mapping from name to identifiers.
export default class Scope {
constructor () {
this.ids = [];
this.names = {};
this.names = blank();
this.used = blank();
}
// Binds the `name` to the given reference `ref`.
bind ( name, ref ) {
if ( isntReference( ref ) ) {
throw new TypeError( `` );
}
this.ids[ this.index( name ) ] = ref;
}
// Deconflict all names within the scope,
// using the given renaming function.
// If no function is supplied, the name is simply prefixed with '_'.
deconflict ( rename = underscorePrefix ) {
const names = blank();
const names = this.used;
this.ids.filter( isntReference ).forEach( id => {
let name = id.name;
@ -45,34 +56,61 @@ export default class Scope {
while ( name in names && names[ name ] !== id ) {
name = rename( name );
}
names[ name ] = id;
id.name = name;
});
}
define ( name, id ) {
this.ids[ this.index( name ) ] = id || new Identifier( name );
// Defines `name` in the scope. `name` must be a `string` or an `Identifier`.
define ( name ) {
if ( typeof name === 'string' ) {
this.ids[ this.index( name ) ] = new Identifier( name );
} else {
this.ids[ this.index( name.name ) ] = name;
}
}
// !! private, don't use !!
//
// Lookup the `ids` index of `name`.
index ( name ) {
if ( !( name in this.names ) ) {
return this.names[ name ] = 1 + this.ids.push();
// The `undefined` value of this push is a placeholder
// that should be overwritten by the caller.
return this.names[ name ] = this.ids.push();
}
return this.names[ name ];
}
// Lookup the identifier referred to by `name`.
lookup ( name ) {
let id = this.ids[ this.names[ name ] ];
while ( id instanceof Reference ) {
id = id.deref();
id = dereference( id );
}
return id;
}
// Get a reference to the identifier `name` in this scope.
reference ( name ) {
return new Reference( this, this.names[ name ] );
}
// Return the names currently in use in the scope.
// Names aren't considered used until they're deconflicted.
usedNames () {
return keys( this.used ).sort();
}
// Create and return a virtual scope, bound to
// the actual scope of this Scope.
virtual () {
const scope = new Scope();
scope.ids = this.ids;
return scope;
}
}

74
test/testScope.js

@ -0,0 +1,74 @@
require('babel/register');
var assert = require( 'assert' );
var Scope = require( '../src/Scope' );
describe( 'Scope', function () {
it( 'can define and bind names', function () {
const scope = new Scope();
// If I define 'a'...
scope.define( 'a' );
// ... and bind 'b' to a reference to 'a'...
scope.bind( 'b', scope.reference( 'a' ) );
// ... lookups for 'a' and 'b' should both
// resolve to the same identifier.
assert.equal( scope.lookup( 'b' ), scope.lookup( 'a' ) );
});
describe( 'virtual scope:', function () {
var real, a, b;
beforeEach(function () {
real = new Scope();
a = real.virtual();
b = real.virtual();
});
it( 'is created within another scope', function () {
// The actual ids are the same.
assert.equal( real.ids, a.ids );
assert.equal( real.ids, b.ids );
});
it( 'lookups different identifiers', function () {
// If I define 'a' in both scopes...
a.define( 'a' );
b.define( 'a' );
// ... the name 'a' should lookup different identifiers.
assert.notEqual( a.lookup( 'a' ), b.lookup( 'b' ) );
});
it( 'can deconflict names', function () {
a.define( 'a' );
b.define( 'a' );
// Deconflicting the actual scope should make all identifiers unique.
real.deconflict();
assert.deepEqual( real.usedNames(), [ '_a', 'a' ] );
});
it( 'deconflicts with a custom function, if provided', function () {
for (var i = 0; i < 26; i++) {
// Create 26 scopes, all of which define 'a'.
real.virtual().define( 'a' );
}
// Custom deconfliction function which ignores the current name.
var num = 10;
real.deconflict( function () {
return (num++).toString(36);
});
assert.deepEqual( real.usedNames(), 'abcdefghijklmnopqrstuvwxyz'.split('') );
// Deconflicting twice has no additional effect.
real.deconflict();
assert.deepEqual( real.usedNames(), 'abcdefghijklmnopqrstuvwxyz'.split('') );
});
});
});
Loading…
Cancel
Save