[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 7c8e32c 062/271: Restore microooptimized, add more benchma
From: |
Jackson Ray Hamilton |
Subject: |
[elpa] master 7c8e32c 062/271: Restore microooptimized, add more benchmarks, add tests. |
Date: |
Thu, 05 Feb 2015 18:29:47 +0000 |
branch: master
commit 7c8e32c2ab1da09dbd82a9a7f395c829022766dc
Author: Jackson Ray Hamilton <address@hidden>
Commit: Jackson Ray Hamilton <address@hidden>
Restore microooptimized, add more benchmarks, add tests.
---
benchmark/scenarios.js | 35 +++---
scopifier-microoptimized.js | 123 ++++++++++++++++++++
test/.jslintrc | 4 +
test/fixtures/monad.js | 12 --
test/fixtures/vow.js | 270 +++++++++++++++++++++++++++++++++++++++++++
test/fixtures/vow.json | 1 +
test/scopifier.js | 11 --
test/specs.js | 53 +++++++++
8 files changed, 469 insertions(+), 40 deletions(-)
diff --git a/benchmark/scenarios.js b/benchmark/scenarios.js
index f2fa386..879da5c 100644
--- a/benchmark/scenarios.js
+++ b/benchmark/scenarios.js
@@ -3,6 +3,7 @@
var fs = require('fs'),
path = require('path'),
scopifier = require('../scopifier'),
+ scopifierMicrooptimized = require('../scopifier-microoptimized'),
jqueryPath = path.join(__dirname, 'fixtures', 'jquery-2.1.1.js'),
lodashPath = path.join(__dirname, 'fixtures', 'lodash-2.4.1.js'),
@@ -19,21 +20,18 @@ suite('scopifier', function () {
next(error);
});
});
-
before(function (next) {
fs.readFile(lodashPath, 'utf8', function (error, contents) {
lodash = contents;
next(error);
});
});
-
before(function (next) {
fs.readFile(asyncPath, 'utf8', function (error, contents) {
async = contents;
next(error);
});
});
-
before(function (next) {
fs.readFile(mkdirpPath, 'utf8', function (error, contents) {
mkdirp = contents;
@@ -41,20 +39,23 @@ suite('scopifier', function () {
});
});
- bench('jquery', function () {
- scopifier(jquery);
- });
-
- bench('lodash', function () {
- scopifier(lodash);
- });
-
- bench('async', function () {
- scopifier(async);
- });
-
- bench('mkdirp', function () {
- scopifier(mkdirp);
+ [scopifier, scopifierMicrooptimized].forEach(function (scopifier, index) {
+ var message = '';
+ if (index === 1) {
+ message = ' (microoptimized)';
+ }
+ bench('jquery' + message, function () {
+ scopifier(jquery);
+ });
+ bench('lodash' + message, function () {
+ scopifier(lodash);
+ });
+ bench('async' + message, function () {
+ scopifier(async);
+ });
+ bench('mkdirp' + message, function () {
+ scopifier(mkdirp);
+ });
});
});
diff --git a/scopifier-microoptimized.js b/scopifier-microoptimized.js
new file mode 100644
index 0000000..1ef067d
--- /dev/null
+++ b/scopifier-microoptimized.js
@@ -0,0 +1,123 @@
+'use strict';
+
+var escope = require('escope'),
+ esprima = require('esprima');
+
+// Given code, returns an array of `[level, start, end]' tokens for
+// context-coloring.
+module.exports = function (code) {
+ var analyzedScopes,
+ ast,
+ comment,
+ comments,
+ definition,
+ definitions,
+ i,
+ isDefined,
+ j,
+ k,
+ mappedDefinitions,
+ range,
+ reference,
+ references,
+ scope,
+ scopes = [],
+ symbols = [],
+ variable;
+
+ // Gracefully handle parse errors by doing nothing.
+ try {
+ ast = esprima.parse(code, {
+ comment: true,
+ range: true
+ });
+ analyzedScopes = escope.analyze(ast).scopes;
+ } catch (error) {
+ process.exit(1);
+ }
+
+ for (i = 0; i < analyzedScopes.length; i += 1) {
+ scope = analyzedScopes[i];
+ // Having its level set implies it was already annotated.
+ if (scope.level === undefined) {
+ if (scope.upper) {
+ if (scope.upper.functionExpressionScope) {
+ // Pretend function expression scope doesn't exist.
+ scope.level = scope.upper.level;
+ scope.variables =
scope.upper.variables.concat(scope.variables);
+ } else {
+ scope.level = scope.upper.level + 1;
+ }
+ } else {
+ // Base case.
+ scope.level = 0;
+ }
+ // We've only given the scope a level for posterity's sake. We're
+ // done now.
+ if (!scope.functionExpressionScope) {
+ range = scope.block.range;
+ scopes.push([
+ scope.level,
+ range[0] + 1,
+ range[1] + 1
+ ]);
+ definitions = [];
+ for (j = 0; j < scope.variables.length; j += 1) {
+ variable = scope.variables[j];
+ mappedDefinitions = [];
+ for (k = 0; k < variable.defs.length; k += 1) {
+ definition = variable.defs[k];
+ range = definition.name.range;
+ mappedDefinitions.push([
+ scope.level,
+ range[0] + 1,
+ range[1] + 1
+ ]);
+ }
+ Array.prototype.push.apply(definitions, mappedDefinitions);
+ }
+ references = [];
+ for (j = 0; j < scope.references.length; j += 1) {
+ reference = scope.references[j];
+ range = reference.identifier.range;
+ isDefined = false;
+ // Determine if a definition already exists for the
+ // range. (escope detects variables twice if they are
+ // declared and initialized simultaneously; this filters
+ // them.)
+ for (k = 0; k < definitions.length; k += 1) {
+ definition = definitions[k];
+ if (definition[1] === range[0] + 1 &&
+ definition[2] === range[1] + 1) {
+ isDefined = true;
+ break;
+ }
+ }
+ if (!isDefined) {
+ references.push([
+ // Handle global references too.
+ reference.resolved ?
reference.resolved.scope.level : 0,
+ range[0] + 1,
+ range[1] + 1
+ ]);
+ }
+ }
+ Array.prototype.push.apply(symbols, definitions);
+ Array.prototype.push.apply(symbols, references);
+ }
+ }
+ }
+
+ comments = [];
+ for (i = 0; i < ast.comments.length; i += 1) {
+ comment = ast.comments[i];
+ range = comment.range;
+ comments.push([
+ -1,
+ range[0] + 1,
+ range[1] + 1
+ ]);
+ }
+
+ return scopes.concat(symbols).concat(comments);
+};
diff --git a/test/.jslintrc b/test/.jslintrc
index 1202114..539f357 100644
--- a/test/.jslintrc
+++ b/test/.jslintrc
@@ -1,5 +1,9 @@
{
"predef": [
+ "after",
+ "afterEach",
+ "before",
+ "beforeEach",
"describe",
"it"
]
diff --git a/test/fixtures/monad.js b/test/fixtures/monad.js
deleted file mode 100644
index 6366fad..0000000
--- a/test/fixtures/monad.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* A monad. */
-function MONAD() {
- abc = 3;
- return function unit(value) {
- // Some details.
- var monad = Object.create(null);
- monad.bind = function (func) {
- return func(value);
- };
- return monad;
- };
-}
diff --git a/test/fixtures/vow.js b/test/fixtures/vow.js
new file mode 100644
index 0000000..374853e
--- /dev/null
+++ b/test/fixtures/vow.js
@@ -0,0 +1,270 @@
+// vow.js
+// Douglas Crockford
+// 2013-09-20
+
+// Public Domain
+
+/*global setImmediate */
+
+
+var VOW = (function () {
+ 'use strict';
+
+// The VOW object contains a .make function that is used to make vows.
+// It may also contain other useful functions.
+// In some mythologies, 'VOW' is called 'deferrer'.
+
+ function enlighten(queue, fate) {
+
+// enlighten is a helper function of herald and .when. It schedules the
+// processing of all of the resolution functions in either the keepers queue
+// or the breakers queue in later turns with the promise's fate.
+
+ queue.forEach(function (func) {
+ setImmediate(func, fate);
+ });
+ }
+
+ return {
+ make: function make() {
+
+// The make function makes new vows. A vow contains a promise object and the
+// two resolution functions (break and keep) that determine the fate of the
+// promise.
+
+ var breakers = [], // .when's broken queue
+ fate, // The promise's ultimate value
+ keepers = [], // .when's kept queue
+ status = 'pending'; // 'broken', 'kept', or 'pending'
+
+ function enqueue(
+ resolution, // 'keep' or 'break'
+ func, // A function that was registered with .when
+ vow // A vow that provides the resolution functions
+ ) {
+
+// enqueue is a helper function used by .when. It will append a function to
+// either the keepers queue or the breakers queue.
+
+ var queue = resolution === 'keep' ? keepers : breakers;
+ queue[queue.length] = typeof func !== 'function'
+
+// If func is not a function, push the resolver so that the value passes to
+// the next cascaded .when.
+
+ ? vow[resolution]
+
+// If the func is a function, push a function that calls func with a value.
+// The result can be a promise, or not a promise, or an exception.
+
+ : function (value) {
+ try {
+ var result = func(value);
+
+// If the result is a promise, then register our resolver with that promise.
+
+ if (result && result.is_promise === true) {
+ result.when(vow.keep, vow.break);
+
+// But if it is not a promise, then use the result to resolve our promise.
+
+ } else {
+ vow.keep(result);
+ }
+
+// But if func throws an exception, then break our promise.
+
+ } catch (e) {
+ vow.break(e);
+ }
+ };
+ }
+
+ function herald(state, value, queue) {
+
+// The herald function is a helper function of break and keep.
+// It seals the promise's fate, updates its status, enlightens
+// one of the queues, and empties both queues.
+
+ if (status !== 'pending') {
+ throw "overpromise";
+ }
+ fate = value;
+ status = state;
+ enlighten(queue, fate);
+ keepers.length = 0;
+ breakers.length = 0;
+ }
+
+// Construct and return the vow object.
+
+ return {
+ 'break': function (value) {
+
+// The break method breaks the promise.
+
+ herald('broken', value, breakers);
+ },
+ keep: function keep(value) {
+
+// The keep method keeps the promise.
+
+ herald('kept', value, keepers);
+ },
+ promise: {
+
+// The promise is an object with a .when method.
+
+ is_promise: true,
+
+// The .when method is the promise monad's bind. The .when method can take two
+// optional functions. One of those functions may be called, depending on the
+// promise's resolution. Both could be called if the the kept function throws.
+
+ when: function (kept, broken) {
+
+// Make a new vow. Return the new promise.
+
+ var vow = make();
+ switch (status) {
+
+// If this promise is still pending, then enqueue both kept and broken.
+
+ case 'pending':
+ enqueue('keep', kept, vow);
+ enqueue('break', broken, vow);
+ break;
+
+// If the promise has already been kept, then enqueue only the kept function,
+// and enlighten it.
+
+ case 'kept':
+ enqueue('keep', kept, vow);
+ enlighten(keepers, fate);
+ break;
+
+// If the promise has already been broken, then enqueue only the broken
+// function, and enlighten it.
+
+ case 'broken':
+ enqueue('break', broken, vow);
+ enlighten(breakers, fate);
+ break;
+ }
+ return vow.promise;
+ }
+ }
+ };
+ },
+ every: function every(array) {
+
+// The every function takes an array of promises and returns a promise that
+// will deliver an array of results only if every promise is kept.
+
+ var remaining = array.length, results = [], vow = VOW.make();
+
+ if (!remaining) {
+ vow.break(array);
+ } else {
+ array.forEach(function (promise, i) {
+ promise.when(function (value) {
+ results[i] = value;
+ remaining -= 1;
+ if (remaining === 0) {
+ vow.keep(results);
+ }
+ }, function (reason) {
+ remaining = NaN;
+ vow.break(reason);
+ });
+ });
+ }
+ return vow.promise;
+ },
+ first: function first(array) {
+
+// The first function takes an array of promises and returns a promise to
+// deliver the first observed kept promise, or a broken promise if all of
+// the promises are broken.
+
+ var found = false, remaining = array.length, vow = VOW.make();
+
+ function check() {
+ remaining -= 1;
+ if (remaining === 0 && !found) {
+ vow.break();
+ }
+ }
+
+ if (remaining === 0) {
+ vow.break(array);
+ } else {
+ array.forEach(function (promise) {
+ promise.when(function (value) {
+ if (!found) {
+ found = true;
+ vow.keep(value);
+ }
+ check();
+ }, check);
+ });
+ }
+ return vow.promise;
+ },
+ any: function any(array) {
+
+// The any function takes an array of promises and returns a promise that
+// will deliver a possibly sparse array of results of any kept promises.
+// The result will contain an undefined element for each broken promise.
+
+ var remaining = array.length, results = [], vow = VOW.make();
+
+ function check() {
+ remaining -= 1;
+ if (remaining === 0) {
+ vow.keep(results);
+ }
+ }
+
+ if (!remaining) {
+ vow.keep(results);
+ } else {
+ array.forEach(function (promise, i) {
+ promise.when(function (value) {
+ results[i] = value;
+ check();
+ }, check);
+ });
+ }
+ return vow.promise;
+ },
+ kept: function (value) {
+
+// Returns a new kept promise.
+
+ var vow = VOW.make();
+ vow.keep(value);
+ return vow.promise;
+ },
+ broken: function (reason) {
+
+// Returns a new broken promise.
+
+ var vow = VOW.make();
+ vow.break(reason);
+ return vow.promise;
+ }
+ };
+}());
+
+
+// If your system does not have setImmediate, then simulate it with setTimeout.
+
+if (typeof setImmediate !== 'function') {
+ setImmediate = function setImmediate(func, param) {
+ 'use strict';
+ return setTimeout(function () {
+ func(param);
+ }, 0);
+ };
+}
diff --git a/test/fixtures/vow.json b/test/fixtures/vow.json
new file mode 100644
index 0000000..8eb2d6d
--- /dev/null
+++ b/test/fixtures/vow.json
@@ -0,0 +1 @@
+[[0,92,8470],[1,103,8174],[2,311,656],[3,583,648],[2,685,5093],[3,1155,2682],[4,1964,2667],[5,2566,2645],[3,2696,3201],[3,3290,3423],[3,3447,3579],[3,3961,5050],[2,5110,5975],[3,5469,5917],[4,5526,5770],[4,5772,5897],[2,5992,6931],[3,6282,6446],[3,6568,6873],[4,6622,6846],[2,6946,7814],[3,7277,7437],[3,7555,7756],[4,7612,7729],[2,7830,7986],[2,8004,8165],[1,8323,8467],[2,8408,8456],[0,96,99],[0,8273,8285],[0,8308,8320],[1,320,329],[2,330,335],[2,337,341],[2,569,574],[3,593,597],[0,613,62
[...]
diff --git a/test/scopifier.js b/test/scopifier.js
deleted file mode 100644
index 3f87ef4..0000000
--- a/test/scopifier.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-var assert = require('assert');
-
-describe('scopifier', function () {
-
- it('should work', function () {
- assert.strictEqual(true, true);
- });
-
-});
diff --git a/test/specs.js b/test/specs.js
new file mode 100644
index 0000000..c015ddb
--- /dev/null
+++ b/test/specs.js
@@ -0,0 +1,53 @@
+'use strict';
+
+var assert = require('assert'),
+ fs = require('fs'),
+ path = require('path'),
+
+ scopifier = require('../scopifier'),
+ scopifierMicrooptimized = require('../scopifier-microoptimized'),
+
+ inputPath = path.join(__dirname, 'fixtures', 'vow.js'),
+ outputPath = path.join(__dirname, 'fixtures', 'vow.json');
+
+describe('scopifier', function () {
+
+ var input, output;
+
+ before(function (done) {
+ fs.readFile(inputPath, 'utf8', function (error, contents) {
+ if (error) {
+ done(error);
+ return;
+ }
+ input = contents;
+ done();
+ });
+ });
+
+ before(function (done) {
+ fs.readFile(outputPath, 'utf8', function (readError, contents) {
+ if (readError) {
+ done(readError);
+ return;
+ }
+ try {
+ output = JSON.parse(contents);
+ } catch (parseError) {
+ done(parseError);
+ }
+ done();
+ });
+ });
+
+ [scopifier, scopifierMicrooptimized].forEach(function (scopifier, index) {
+ var message = '';
+ if (index === 1) {
+ message = ' (microoptimized)';
+ }
+ it('should work' + message, function () {
+ assert.deepEqual(scopifier(input), output);
+ });
+ });
+
+});
- [elpa] master 0e64e85 055/271: Simplify to 2 arrays., (continued)
- [elpa] master 0e64e85 055/271: Simplify to 2 arrays., Jackson Ray Hamilton, 2015/02/05
- [elpa] master ff988f3 051/271: Fix duplicate tokens and tail-end bug., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 0143a29 058/271: Leverage .jslintrc files., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 38f0821 059/271: Perf with ugly for loops., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 148e55b 046/271: Readme updates., Jackson Ray Hamilton, 2015/02/05
- [elpa] master a02fe9f 064/271: Switch to microoptimized., Jackson Ray Hamilton, 2015/02/05
- [elpa] master f07ce20 061/271: Revert back to functions., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 0ab3ed5 060/271: And it still made no difference., Jackson Ray Hamilton, 2015/02/05
- [elpa] master c2114e9 040/271: Uglify tokenizer now working., Jackson Ray Hamilton, 2015/02/05
- [elpa] master b1e7a1d 076/271: Switch to 1-dimensional array., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 7c8e32c 062/271: Restore microooptimized, add more benchmarks, add tests.,
Jackson Ray Hamilton <=
- [elpa] master eb2e6b3 070/271: Formalize italic support., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 67637bf 072/271: Update test fixture., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 65e4763 065/271: Just push to a single tokens array., Jackson Ray Hamilton, 2015/02/05
- [elpa] master cb8b1f2 066/271: Add light color scheme., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 7a49f9f 067/271: Make comments italic., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 07fe42c 078/271: Vconcat., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 28667b8 075/271: Move microoptimized scopifier into the limelight., Jackson Ray Hamilton, 2015/02/05
- [elpa] master c1f9f11 063/271: Switch to concat., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 183ac4e 081/271: Add emacs benchmark., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 59b1c99 074/271: Documentation and cleanup., Jackson Ray Hamilton, 2015/02/05