Permalink
Browse files

coroutine hooks

  • Loading branch information...
1 parent 715168c commit 8cbce2b0db59a248fd97b0391e689f60e8a57db7 @retroj retroj committed Feb 13, 2010
Showing with 235 additions and 73 deletions.
  1. +199 −73 modules/hook.js
  2. +36 −0 tests/simple/hook.js
View
272 modules/hook.js
@@ -1,10 +1,13 @@
/**
* (C) Copyright 2007-2008 Jeremy Maitin-Shepard
+ * (C) Copyright 2010 John J. Foerch
*
* Use, modification, and distribution are subject to the terms specified in the
* COPYING file.
**/
+require("coroutine.js");
+
/* Adds the specified function to the specified hook. To add a local
* hook, invoke this function as: add_hook.call(context, hook_name, ...).
* Note: hook_name must be a string */
@@ -54,119 +57,242 @@ function run_hooks_until_failure (hook, args) {
return true;
}
-var hook_type_doc_strings = [
- /* RUN_HOOK */
- "Each hook function is run in sequence.",
+function run_coroutine_hooks (hook, args) {
+ if (hook == null)
+ yield co_return();
+ for (let i = 0, hlen = hook.length; i < hlen; ++i)
+ yield hook[i].apply(null, Array.prototype.slice.call(args));
+}
- /* RUN_HOOK_UNTIL_SUCCESS */
- "Each hook function is run in sequence until one returns a "+
- "logically true value.",
+function run_coroutine_hooks_until_success (hook, args) {
+ if (hook == null)
+ yield co_return(false);
+ for (let i = 0, hlen = hook.length; i < hlen; ++i)
+ if ((yield hook[i].apply(null, Array.prototype.slice.call(args))))
+ yield co_return(true);
+ yield co_return(false);
+}
- /* RUN_HOOK_UNTIL_FAILURE */
- "Each hook function is run in sequence until one returns a "+
- "logically false value. If no function returns such a "+
- "value, then the result of the hook will be `true'."];
+function run_coroutine_hooks_until_failure (hook, args) {
+ if (hook == null)
+ yield co_return(true);
+ for (let i = 0, hlen = hook.length; i < hlen; ++i)
+ if (!(yield hook[i].apply(null, Array.prototype.slice.call(args))))
+ yield co_return(false);
+ yield co_return(true);
+}
+
+
+const RUN_HOOK = 'RUN_HOOK';
+const RUN_HOOK_UNTIL_SUCCESS = 'RUN_HOOK_UNTIL_SUCCESS';
+const RUN_HOOK_UNTIL_FAILURE = 'RUN_HOOK_UNTIL_FAILURE';
/* This should only be used by define_hook functions */
-function initialize_hook (prototype, hook_name, hook_type, doc_string, extra_doc_string) {
+function initialize_hook (run, hook_name, hook_type, doc_string, extra_doc_string) {
+ var docstrings = {
+ RUN_HOOK: "Each hook function is run in sequence.",
+ RUN_HOOK_UNTIL_SUCCESS:
+ "Each hook function is run in sequence until one returns a "+
+ "logically true value.",
+ RUN_HOOK_UNTIL_FAILURE:
+ "Each hook function is run in sequence until one returns a "+
+ "logically false value. If no function returns such a "+
+ "value, then the result of the hook will be `true'."
+ };
var h = this[hook_name];
if (h == null)
h = this[hook_name] = [];
if (hook_type == null)
hook_type = RUN_HOOK;
- switch (hook_type) {
- case RUN_HOOK:
- h.run = prototype.run;
- break;
- case RUN_HOOK_UNTIL_SUCCESS:
- h.run = prototype.run_until_success;
- break;
- case RUN_HOOK_UNTIL_FAILURE:
- h.run = prototype.run_until_failure;
- break;
- }
+ h.run = run;
h.hook_type = hook_type;
h.hook_name = hook_name;
h.doc_string =
(doc_string? doc_string + "\n" : "") +
- hook_type_doc_strings[hook_type] +
+ docstrings[hook_type] +
(extra_doc_string? "\n" + extra_doc_string : "");
h.source_code_reference = get_caller_source_code_reference(1);
return h;
}
-var hook_global_prototype = {
- run: function () {
- run_hooks(this, arguments);
- },
- run_until_success: function () {
- return run_hooks_until_success(this, arguments);
- },
- run_until_failure: function () {
- return run_hooks_until_failure(this, arguments);
- }
-};
-
-const RUN_HOOK = 0;
-const RUN_HOOK_UNTIL_SUCCESS = 1;
-const RUN_HOOK_UNTIL_FAILURE = 2;
-
function define_hook (hook_name, hook_type, doc_string) {
- initialize_hook(hook_global_prototype, hook_name, hook_type, doc_string);
+ const prototype = {
+ RUN_HOOK: function () {
+ run_hooks(this, arguments);
+ },
+ RUN_HOOK_UNTIL_SUCCESS: function () {
+ return run_hooks_until_success(this, arguments);
+ },
+ RUN_HOOK_UNTIL_FAILURE: function () {
+ return run_hooks_until_failure(this, arguments);
+ }
+ };
+ initialize_hook(prototype[hook_type || RUN_HOOK],
+ hook_name, hook_type, doc_string);
}
-var hook_simple_local_prototype = {
- run: function (x) {
- var hook_name = this.hook_name;
- if (hook_name in x) run_hooks(x[hook_name], arguments);
- run_hooks(this, arguments);
- },
- run_until_success: function (x) {
- var hook_name = this.hook_name;
- if ((hook_name in x) && run_hooks_until_success(x[hook_name], arguments)) return true;
- return run_hooks_until_success(conkeror[hook_name], arguments);
- },
- run_until_failure: function (x) {
- var hook_name = this.hook_name;
- if ((hook_name in x) && !run_hooks_until_success(x[hook_name], arguments)) return false;
- return run_hooks_until_failure(conkeror[hook_name], arguments);
- }
-};
+function define_coroutine_hook (hook_name, hook_type, doc_string) {
+ const prototype = {
+ RUN_HOOK: function () {
+ yield run_coroutine_hooks(this, arguments);
+ },
+ RUN_HOOK_UNTIL_SUCCESS: function () {
+ var result = yield run_coroutine_hooks_until_success(this, arguments);
+ yield co_return(result);
+ },
+ RUN_HOOK_UNTIL_FAILURE: function () {
+ var result = yield run_coroutine_hooks_until_failure(this, arguments);
+ yield co_return(result);
+ }
+ };
+ initialize_hook(prototype[hook_type || RUN_HOOK],
+ hook_name, hook_type, doc_string);
+}
function simple_local_hook_definer (extra_doc_string) {
+ const prototype = {
+ RUN_HOOK: function (x) {
+ var hook_name = this.hook_name;
+ if (hook_name in x)
+ run_hooks(x[hook_name], arguments);
+ run_hooks(this, arguments);
+ },
+ RUN_HOOK_UNTIL_SUCCESS: function (x) {
+ var hook_name = this.hook_name;
+ if ((hook_name in x) && run_hooks_until_success(x[hook_name], arguments))
+ return true;
+ return run_hooks_until_success(conkeror[hook_name], arguments);
+ },
+ RUN_HOOK_UNTIL_FAILURE: function (x) {
+ var hook_name = this.hook_name;
+ if ((hook_name in x) && !run_hooks_until_success(x[hook_name], arguments))
+ return false;
+ return run_hooks_until_failure(conkeror[hook_name], arguments);
+ }
+ };
return function (hook_name, hook_type, doc_string) {
- initialize_hook(hook_simple_local_prototype, hook_name, hook_type, doc_string, extra_doc_string);
+ initialize_hook(prototype[hook_type || RUN_HOOK],
+ hook_name, hook_type, doc_string,
+ extra_doc_string);
+ };
+}
+
+function simple_local_coroutine_hook_definer (extra_doc_string) {
+ const prototype = {
+ RUN_HOOK: function (x) {
+ var hook_name = this.hook_name;
+ if (hook_name in x)
+ yield run_coroutine_hooks(x[hook_name], arguments);
+ yield run_coroutine_hooks(this, arguments);
+ },
+ RUN_HOOK_UNTIL_SUCCESS: function (x) {
+ var hook_name = this.hook_name;
+ if ((hook_name in x) &&
+ (yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
+ {
+ yield co_return(true);
+ }
+ var result = yield run_coroutine_hooks_until_success(conkeror[hook_name], arguments);
+ yield co_return(result);
+ },
+ RUN_HOOK_UNTIL_FAILURE: function (x) {
+ var hook_name = this.hook_name;
+ if ((hook_name in x) &&
+ !(yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
+ {
+ yield co_return(false);
+ }
+ var result = yield run_coroutine_hooks_until_failure(conkeror[hook_name], arguments);
+ yield co_return(result);
+ }
+ };
+ return function (hook_name, hook_type, doc_string) {
+ initialize_hook(prototype[hook_type || RUN_HOOK],
+ hook_name, hook_type, doc_string,
+ extra_doc_string);
};
}
-/* This function is called with a variable number of string arguments
- * in addition to the first, that specify the additional hook arrays
- * to use. As an example: local_hook_definer("buffer", "buffer", "buffer.window")
- */
function local_hook_definer (prop_name, extra_doc_string) {
- var prototype = {
- run: function (x) {
+ const prototype = {
+ RUN_HOOK: function (x) {
var hook_name = this.hook_name;
- if (hook_name in x) run_hooks(x[hook_name], arguments);
- if (hook_name in x[prop_name]) run_hooks(x[prop_name][hook_name], arguments);
+ if (hook_name in x)
+ run_hooks(x[hook_name], arguments);
+ if (hook_name in x[prop_name])
+ run_hooks(x[prop_name][hook_name], arguments);
run_hooks(this, arguments);
},
- run_until_success: function (x) {
+ RUN_HOOK_UNTIL_SUCCESS: function (x) {
var hook_name = this.hook_name;
- if ((hook_name in x) && run_hooks_until_success(x[hook_name], arguments)) return true;
- if ((hook_name in x[prop_name]) && run_hooks_until_success(x[prop_name][hook_name], arguments)) return true;
+ if ((hook_name in x) && run_hooks_until_success(x[hook_name], arguments))
+ return true;
+ if ((hook_name in x[prop_name]) && run_hooks_until_success(x[prop_name][hook_name], arguments))
+ return true;
return run_hooks_until_success(conkeror[hook_name], arguments);
},
- run_until_failure: function (x) {
+ RUN_HOOK_UNTIL_FAILURE: function (x) {
var hook_name = this.hook_name;
- if ((hook_name in x) && !run_hooks_until_success(x[hook_name], arguments)) return false;
- if ((hook_name in x[prop_name]) && !run_hooks_until_success(x[prop_name][hook_name], arguments)) return false;
+ if ((hook_name in x) && !run_hooks_until_success(x[hook_name], arguments))
+ return false;
+ if ((hook_name in x[prop_name]) && !run_hooks_until_success(x[prop_name][hook_name], arguments))
+ return false;
return run_hooks_until_failure(conkeror[hook_name], arguments);
}
};
return function (hook_name, hook_type, doc_string) {
- initialize_hook(prototype, hook_name, hook_type, doc_string, extra_doc_string);
+ initialize_hook(prototype[hook_type || RUN_HOOK],
+ hook_name, hook_type, doc_string,
+ extra_doc_string);
+ };
+}
+
+function local_coroutine_hook_definer (prop_name, extra_doc_string) {
+ const prototype = {
+ RUN_HOOK: function (x) {
+ var hook_name = this.hook_name;
+ if (hook_name in x)
+ yield run_coroutine_hooks(x[hook_name], arguments);
+ if (hook_name in x[prop_name])
+ yield run_coroutine_hooks(x[prop_name][hook_name], arguments);
+ yield run_coroutine_hooks(this, arguments);
+ },
+ RUN_HOOK_UNTIL_SUCCESS: function (x) {
+ var hook_name = this.hook_name;
+ if ((hook_name in x) &&
+ (yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
+ {
+ yield co_return(true);
+ }
+ if ((hook_name in x[prop_name]) &&
+ (yield run_coroutine_hooks_until_success(x[prop_name][hook_name], arguments)))
+ {
+ yield co_return(true);
+ }
+ var result = yield run_coroutine_hooks_until_success(conkeror[hook_name], arguments);
+ yield co_return(result);
+ },
+ RUN_HOOK_UNTIL_FAILURE: function (x) {
+ var hook_name = this.hook_name;
+ if ((hook_name in x) &&
+ !(yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
+ {
+ yield co_return(false);
+ }
+ if ((hook_name in x[prop_name]) &&
+ !(yield run_coroutine_hooks_until_success(x[prop_name][hook_name], arguments)))
+ {
+ yield co_return(false);
+ }
+ var result = yield run_coroutine_hooks_until_failure(conkeror[hook_name], arguments);
+ yield co_return(result);
+ }
+ };
+ return function (hook_name, hook_type, doc_string) {
+ initialize_hook(prototype[hook_type || RUN_HOOK],
+ hook_name, hook_type, doc_string,
+ extra_doc_string);
};
}
View
36 tests/simple/hook.js
@@ -0,0 +1,36 @@
+
+require("walnut.js");
+
+// coroutine hook tests
+walnut_run ({
+ suite_setup: function () {},
+ suite_teardown: function () {},
+ setup: function () {
+ define_coroutine_hook("test_hook");
+ },
+ teardown: function () {
+ test_hook = undefined;
+ },
+ test_synchronous_coroutine_hook: function () {
+ var a = 0;
+ function inc_a () { a += 1; }
+ add_hook("test_hook", inc_a);
+ co_call(test_hook.run());
+ assert_equals(a, 1);
+ },
+ test_asynchronous_coroutine_hook: function () {
+ var cont;
+ var str = "";
+ add_hook("test_hook",
+ function () {
+ str += "a";
+ cont = yield CONTINUATION;
+ str += (yield SUSPEND);
+ });
+ add_hook("test_hook",
+ function () str += "b");
+ co_call(test_hook.run());
+ cont("c");
+ assert_equals(str, "acb");
+ }
+});

0 comments on commit 8cbce2b

Please sign in to comment.