Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Generators-based implementation #9

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
162 changes: 49 additions & 113 deletions dist/lispx-vm-dev.umd.js
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,29 @@ __webpack_require__.r(__webpack_exports__);
*/
function init_control(vm)
{
vm.call_lisp_from_generator = function* (action)
{
while (true) {
const result = action();
if (result instanceof vm.Suspension) {
const resumption = yield result;
action = () => resumption.resume();
} else {
return result;
}
}
};

vm.call_generator_from_lisp = function(generator, resumption = null)
{
const result = generator.next(resumption);
if (result.done) {
return result.value;
} else {
return result.value.suspend((resumption) => vm.call_generator_from_lisp(generator, resumption));
}
}

/*** Continuations ***/

/*
Expand Down Expand Up @@ -1750,28 +1773,14 @@ function init_control(vm)
*/
function LOOP(operands, env)
{
const expr = vm.assert_type(vm.elt(operands, 0), vm.TYPE_ANY);
return do_loop(expr, env);
return vm.call_generator_from_lisp(loop_gen(operands, env));
}

function do_loop(expr, env, resumption = null)
function* loop_gen(operands, env)
{
let first = true; // Only resume once.
while (true) {
let result;
if (first && (resumption instanceof vm.Resumption)) {
first = false;
result = resumption.resume();
} else {
result = vm.eval(expr, env);
}
if (result instanceof vm.Suspension) {
return result.suspend((resumption) =>
do_loop(expr, env, resumption));
} else {
continue;
}
}
const expr = vm.assert_type(vm.elt(operands, 0), vm.TYPE_ANY);
while (true)
yield* vm.call_lisp_from_generator(() => vm.eval(expr, env));
}

/*
Expand Down Expand Up @@ -1865,88 +1874,15 @@ function init_control(vm)
*/
function UNWIND_PROTECT(operands, env)
{
const protected_expr = vm.assert_type(vm.elt(operands, 0), vm.TYPE_ANY);
const cleanup_expr = vm.assert_type(vm.elt(operands, 1), vm.TYPE_ANY);
return do_unwind_protect_1(protected_expr, cleanup_expr, env);
return vm.call_generator_from_lisp(unwind_protect_gen(operands, env));
}

/*
* This must be implemented in two steps, with two work functions.
*
* The first one evaluates the protected expression, which may (a)
* return normally, or (b) exit nonlocally with an exception, or (c)
* capture a continuation.
*
* If it does capture, we'll have to restart at step 1 later. If
* it does not capture, we can go to step 2, but have to remember
* whether step 1 returned successfully or threw an exception.
*
* The second work function, step 2, evaluates the cleanup
* expression and afterwards either returns the result produced by
* the protected expression, or (re)throws the exception
* thrown by it.
*/
function do_unwind_protect_1(protected_expr, cleanup_expr, env, resumption = null)
function* unwind_protect_gen(operands, env)
{
try {
let result;
if (resumption instanceof vm.Resumption)
result = resumption.resume();
else
result = vm.eval(protected_expr, env);
if (result instanceof vm.Suspension)
/*
* (c) Protected expression captured - stay at step 1.
*/
return result.suspend((resumption) =>
do_unwind_protect_1(protected_expr, cleanup_expr, env, resumption));
else
/*
* (a) Protected expression returned normally - go to step 2,
* remembering that step 1 was successful.
*/
return do_unwind_protect_2(cleanup_expr, result, true, env);
} catch (exception) {
/*
* (b) Protected expression threw - go to step 2,
* remembering that step 1 failed.
*/
return do_unwind_protect_2(cleanup_expr, exception, false, env);
}
}

/*
* Second step of UNWIND-PROTECT. We evaluate the cleanup
* expression, which of course may suspend, itself.
*
* Afterwards we either return the result of the protected
* expression, or rethrow the exception thrown by it.
*/
function do_unwind_protect_2(cleanup_expr, value, success, env, resumption = null)
{
let result;
if (resumption instanceof vm.Resumption)
result = resumption.resume();
else
result = vm.eval(cleanup_expr, env);
if (result instanceof vm.Suspension) {
return result.suspend((resumption) =>
do_unwind_protect_2(cleanup_expr, value, success, env, resumption));
} else {
/*
* After the cleanup expression has been evaluated:
*
* If the protected expression returned normally (a),
* return its result now.
*
* If it threw an exception (b), rethrow the exception
* now.
*/
if (success)
return value;
else
throw value;
}
const protected_expr = vm.assert_type(vm.elt(operands, 0), vm.TYPE_ANY);
const cleanup_expr = vm.assert_type(vm.elt(operands, 1), vm.TYPE_ANY);
try { return yield* vm.call_lisp_from_generator(() => vm.eval(protected_expr, env)); }
finally { yield* vm.call_lisp_from_generator(() => vm.eval(cleanup_expr, env)); }
}

/*** Root Prompt and Evaluation Entry Point ***/
Expand All @@ -1970,11 +1906,10 @@ function init_control(vm)
* traces).
*/
vm.eval_form = (form, env = vm.get_user_environment()) =>
vm.push_subcont_barrier(() =>
vm.push_prompt(ROOT_PROMPT,
() => vm.eval(form, env),
env),
env);
vm.push_subcont_barrier(() => vm.eval_form_core(form, env), env);

vm.eval_form_core = (form, env = vm.get_user_environment()) =>
vm.push_prompt(ROOT_PROMPT, () => vm.eval(form, env), env);

/*** Stack Trace ***/

Expand Down Expand Up @@ -2533,20 +2468,21 @@ function init_eval(vm)
* result.
*/
function IF(operands, env)
{
return vm.call_generator_from_lisp(if_gen(operands, env));
}

function* if_gen(operands, env)
{
const test = vm.elt(operands, 0);
const consequent = vm.elt(operands, 1);
const alternative = vm.elt(operands, 2);

return vm.bind(() => vm.eval(test, env),
(result) => {
vm.assert_type(result, vm.Boolean);
if (result == vm.t())
return vm.eval(consequent, env);
else
return vm.eval(alternative, env);
},
vm.trace(test, env));
const result = yield* vm.call_lisp_from_generator(() => vm.eval(test, env));
vm.assert_type(result, vm.Boolean);
if (result === vm.t())
return yield* vm.call_lisp_from_generator(() => vm.eval(consequent, env));
else
return yield* vm.call_lisp_from_generator(() => vm.eval(alternative, env));
}

/*** Exception Trapping and Panicking ***/
Expand Down
2 changes: 1 addition & 1 deletion dist/lispx-vm.min.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/lispx-vm.umd.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/repl/node/repl.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/repl/web/repl.umd.js

Large diffs are not rendered by default.