Мне нужны некоторые хитрости, как мне нужно реализовать и реализовать продолжение для губ в JavaScript (мой lisp почти как схема, за исключением без продолжений и оглавления).
Вот моя функция оценки:
function getFunctionArgs(rest, { env, dynamic_scope, error }) {
var args = [];
var node = rest;
markCycles(node);
while (true) {
if (node instanceof Pair && !isEmptyList(node)) {
var arg = evaluate(node.car, { env, dynamic_scope, error });
if (dynamic_scope) {
arg = unpromise(arg, arg => {
if (typeof arg === 'function' && isNativeFunction(arg)) {
return arg.bind(dynamic_scope);
}
return arg;
});
}
args.push(arg);
if (node.haveCycles('cdr')) {
break;
}
node = node.cdr;
} else {
break;
}
}
return resolvePromises(args);
}
// -------------------------------------------------------------------------
function evaluateMacro(macro, code, eval_args) {
if (code instanceof Pair) {
//code = code.clone();
}
var value = macro.invoke(code, eval_args);
return unpromise(resolvePromises(value), function ret(value) {
if (value && value.data || !value || selfEvaluated(value)) {
return value;
} else {
return quote(evaluate(value, eval_args));
}
});
}
// -------------------------------------------------------------------------
function evaluate(code, { env, dynamic_scope, error = () => {} } = {}) {
try {
if (dynamic_scope === true) {
env = dynamic_scope = env || global_env;
} else if (env === true) {
env = dynamic_scope = global_env;
} else {
env = env || global_env;
}
var eval_args = { env, dynamic_scope, error };
var value;
if (isNull(code)) {
return code;
}
if (isEmptyList(code)) {
return emptyList();
}
if (code instanceof Symbol) {
return env.get(code, { weak: true });
}
var first = code.car;
var rest = code.cdr;
if (first instanceof Pair) {
value = resolvePromises(evaluate(first, eval_args));
if (isPromise(value)) {
return value.then((value) => {
return evaluate(new Pair(value, code.cdr), eval_args);
});
// else is later in code
} else if (typeof value !== 'function') {
throw new Error(
type(value) + ' ' + env.get('string')(value) +
' is not a function while evaluating ' + code.toString()
);
}
}
if (first instanceof Symbol) {
value = env.get(first, { weak: true });
if (value instanceof Macro) {
var ret = evaluateMacro(value, rest, eval_args);
return unpromise(ret, result => {
if (result instanceof Pair) {
return result.markCycles();
}
return result;
});
} else if (typeof value !== 'function') {
if (value) {
var msg = `${type(value)} \`${value}' is not a function`;
throw new Error(msg);
}
throw new Error(`Unknown function \`${first.name}'`);
}
} else if (typeof first === 'function') {
value = first;
}
if (typeof value === 'function') {
var args = getFunctionArgs(rest, eval_args);
return unpromise(args, function(args) {
var scope = dynamic_scope || env;
var result = resolvePromises(value.apply(scope, args));
return unpromise(result, (result) => {
if (result instanceof Pair) {
return quote(result.markCycles());
}
return result;
}, error);
});
} else if (code instanceof Symbol) {
value = env.get(code);
if (value === 'undefined') {
throw new Error('Unbound variable `' + code.name + '\'');
}
return value;
} else if (code instanceof Pair) {
value = first && first.toString();
throw new Error(`${type(first)} ${value} is not a function`);
} else {
return code;
}
} catch (e) {
error && error(e, code);
}
}
ПРИМЕЧАНИЯ:
// unpromise and resolvePromises is just used ot unwrap any promise
// inside list and return new promise for whole expression if found
// any promise and not found it just return value as is
// markCycles is used to prevent of recursive printing of list cycles
// if you create graph cycles using `set-cdr!` or `set-car!`
Нужно ли создавать стек при оценке выражения для продолжений?Как я могу это сделать?Я думал, что я создаю какой-то класс Continuation
, который будет в двух режимах: один в заполненном, когда он может быть вызван как Macro, и в другом, ожидая, когда будет заполнен оператором оценки с кодом, который должен быть выполнен, я также не уверен, какя должен идти и не оценивать код перед выражением, которое вызывает продолжение, например:
(* 10 (cont 2))
(* 10 x)
нужно игнорировать
Я также не уверен, как мне поступитьи создайте call/cc
как функцию.Должен ли он возвращать некоторую промежуточную структуру данных с аргументом, хранящимся в этой структуре данных, чтобы он мог быть вызван при помощи метода вычисления с продолжением?
'call/cc': function(lambda) {
return new CallCC(lambda);
}
и если eval найдет экземпляр CallCC, он получит продолжение (пока не уверен, как)используйте
if (value instanceof CallCC) {
value.call(new Continuation(stack));
}
Это как вы это сделаете?В общем, мой вопрос о стеке.Это нужно для продолжения?Если это необходимо, то как это должно быть создано?
Я нашел эту статью Написание Lisp: Продолжения , которые показывают, как реализовать продолжения, но это трудно понять, потому что это на Haskell,