Revision a70843e600bb25b2a0092a00e822400235579b28 authored by Joshua J. Cogliati on 24 April 2020, 20:30:58 UTC, committed by Joshua J. Cogliati on 24 April 2020, 20:30:58 UTC
1 parent 781fb6a
eval.c
/*
* eval.c logo eval/apply module dko
*
* Copyright (C) 1993 by the Regents of the University of California
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#define WANT_EVAL_REGS 1
#include "logo.h"
#include "globals.h"
/* evaluator registers that need saving around evals */
struct registers regs;
NODE *Regs_Node;
int num_saved_nodes;
int inside_evaluator = 0;
NODE *eval_buttonact = NIL;
/* node-value registers that don't need saving */
NODE *expresn = NIL, /* the current expression */
*val = NIL, /* the value of the last expression */
*stack = NIL, /* register stack */
*numstack = NIL,/* stack whose elements aren't objects */
*parm = NIL, /* the current formal */
*catch_tag = NIL,
*arg = NIL; /* the current actual */
NODE
*var_stack = NIL, /* the stack of variables and their bindings */
*last_call = NIL, /* the last proc called */
*output_node = NIL, /* the output of the current function */
*output_unode = NIL; /* the unode in which we saw the output */
#if defined(__GNUC__) && !defined(__clang__)
#define USE_GCC_DISPATCH 1
#endif
#define DEBUGGING 0
#if DEBUGGING
#define DEB_STACK 0 /* set to 1 to log save/restore */
#define DEB_CONT 0 /* set to 1 to log newcont/fetch_cont */
#define do_debug(x) \
x(expresn) x(unev) x(val) x(didnt_get_output) x(didnt_output_name) x(fun) \
x(proc)
#define deb_enum(x) \
ndprintf(stdout, #x " = %s, ", x);
void vs_print() {
FIXNUM vs = val_status;
int i;
static char *vnames[] = {"VALUE_OK", "NO_VALUE_OK", "OUTPUT_OK",
"STOP_OK", "OUTPUT_TAIL", "STOP_TAIL"};
static char *names[] = {"RUN", "STOP", "OUTPUT", "THROWING",
"MACRO_RETURN"};
if (!varTrue(Redefp)) return;
printf("Val_status = ");
for (i=0; i<6; i++) {
if (vs&1) {
printf(vnames[i]);
vs >>= 1;
if (vs != 0) printf("|");
} else vs >>= 1;
if (vs == 0) break;
}
if (vs != 0) printf("0%o", vs<<6);
printf(", stopping_flag = %s\n", names[stopping_flag]);
}
void debprint(char *name) {
if (!varTrue(Redefp)) return;
printf("%s: ", name);
do_debug(deb_enum)
vs_print();
printf("current_unode=0x%x, output_unode=0x%x\n",current_unode,
output_unode);
}
#define debprint2(a,b) if (varTrue(Redefp)) ndprintf(stdout,a,b)
#else
#define debprint(name)
#define debprint2(a,b)
#define DEB_STACK 0
#define DEB_CONT 0
#endif
#ifdef HAVE_TERMIO_H
#ifdef HAVE_WX
#include <termios.h>
#else
#include <termio.h>
#endif
#else
#ifdef HAVE_SGTTY_H
#include <sgtty.h>
#endif
#endif
#if DEB_STACK
NODE *restname, *restline;
#define save(register) ( debprint2("saving " #register " = %s ", register), \
push(register, stack), \
push(make_intnode(__LINE__), stack), \
debprint2(" at line %s\n", car(stack)), \
push(make_static_strnode(#register), stack) )
#define restore(register) ( restname = car(stack), pop(stack), \
restline = car(stack), pop(stack), \
register = car(stack), pop(stack), \
( (strcmp(getstrptr(restname), #register)) ? (\
debprint2("*** Restoring " #register " but saved %s",\
restname), \
debprint2(" at line %s! ***\n", restline) \
) : 0), \
debprint2("restoring " #register " = %s ", register), \
debprint2(" at line %s\n", make_intnode(__LINE__)) )
#define save2(reg1,reg2) ( save(reg1), save(reg2) )
#define restore2(reg1,reg2) ( restore(reg2), restore(reg1) )
#else
#define save(register) push(register, stack)
#define restore(register) (register = car(stack), pop(stack))
#define save2(reg1,reg2) (push(reg1,stack),stack->n_obj=reg2)
#define restore2(reg1,reg2) (reg2 = getobject(stack), \
reg1 = car(stack), pop(stack))
#endif
/* saving and restoring FIXNUMs rather than NODEs */
#define numsave(register) numpush(register,&numstack)
#define numrestore(register) (register=(FIXNUM)car(numstack), numstack=cdr(numstack))
#define num2save(reg1,reg2) (numpush(reg1,&numstack),numstack->n_obj=(NODE *)reg2)
#define num2restore(reg1,reg2) (reg2=(FIXNUM)getobject(numstack), \
reg1=(FIXNUM)car(numstack), numstack=cdr(numstack))
#if DEB_CONT
#define newcont(tag) debprint("Newcont = " #tag); \
numsave(cont); cont = (FIXNUM)tag
#else
#define newcont(tag) (numsave(cont), cont = (FIXNUM)tag)
#endif
/* These variables are all externed in globals.h */
CTRLTYPE stopping_flag = RUN;
char *logolib, *helpfiles, *csls;
FIXNUM dont_fix_ift = 0;
/* These variables are local to this file. */
static int trace_level = 0; /* indentation level when tracing */
/* These first few functions are externed in globals.h */
void numpush(FIXNUM obj, NODE **stack) {
NODE *temp = newnode(CONT); /*GC*/
temp->n_car = (NODE *)obj;
temp->n_cdr = *stack;
*stack = temp;
}
/* forward declaration */
NODE *evaluator(NODE *list, enum labels where);
/* Evaluate a line of input. */
void eval_driver(NODE *line) {
evaluator(line, begin_line);
}
/* Evaluate a sequence of expressions until we get a value to return.
* (Called from erract.)
*/
NODE *err_eval_driver(NODE *seq, BOOLEAN recoverable) {
val_status = (recoverable ? VALUE_OK : NO_VALUE_OK) |
(val_status & (OUTPUT_OK|STOP_OK));
return evaluator(seq, begin_seq);
}
/* The logo word APPLY. */
NODE *lapply(NODE *args) {
return make_cont(begin_apply, args);
}
/* The logo word ? <question-mark>. */
NODE *lqm(NODE *args) {
FIXNUM argnum = 1, i;
NODE *np = qm_list;
if (args != NIL) argnum = getint(pos_int_arg(args));
if (stopping_flag == THROWING) return(UNBOUND);
i = argnum;
while (--i > 0 && np != NIL) np = cdr(np);
if (np == NIL)
return(err_logo(BAD_DATA_UNREC,make_intnode(argnum)));
return(car(np));
}
/* The rest of the functions are local to this file. */
/* Warn the user if a local variable shadows a global one. */
void tell_shadow(NODE *arg) {
if (flag__caseobj(arg, VAL_STEPPED))
err_logo(SHADOW_WARN, arg);
}
/* Check if a local variable is already in this frame */
int not_local(NODE *name, NODE *sp) {
for ( ; sp != var; sp = cdr(sp)) {
if (compare_node(car(sp),name,TRUE) == 0) {
return FALSE;
}
}
return TRUE;
}
/* reverse a list destructively */
NODE *reverse(NODE *list) {
NODE *ret = NIL, *temp;
while (list != NIL) {
temp = list;
list = cdr(list);
setcdr(temp, ret);
ret = temp;
}
return ret;
}
/* nondestructive append */
NODE *append(NODE *a, NODE *b) {
if (a == NIL) return b;
return cons(car(a), append(cdr(a), b));
}
/* nondestructive flatten */
NODE *flatten(NODE *a) {
if (a == NIL) return NIL;
return append(car(a), flatten(cdr(a)));
}
/* Reset the var stack to the previous place holder.
*/
void reset_args(NODE *old_stack) {
for (; var_stack != old_stack; pop(var_stack)) {
if (nodetype(var_stack) & NT_LOCAL)
setflag__caseobj(car(var_stack), IS_LOCAL_VALUE);
else
clearflag__caseobj(car(var_stack), IS_LOCAL_VALUE);
setvalnode__caseobj(car(var_stack), getobject(var_stack));
}
}
NODE *bf3(NODE *name) {
NODE *string = cnv_node_to_strnode(name);
return make_strnode(getstrptr(string)+3, getstrhead(string),
getstrlen(string)-3, nodetype(string), strcpy);
}
NODE *deep_copy(NODE *expresn) {
NODE *val, **p, **q;
FIXNUM arridx;
if (expresn == NIL) return NIL;
else if (is_list(expresn)) {
val = cons(deep_copy(car(expresn)), deep_copy(cdr(expresn)));
val->n_obj = deep_copy(expresn->n_obj);
settype(val, nodetype(expresn));
} else if (nodetype(expresn) == ARRAY) {
val = make_array(getarrdim(expresn));
setarrorg(val, getarrorg(expresn));
for (p = getarrptr(expresn), q = getarrptr(val), arridx=0;
arridx < getarrdim(expresn); arridx++, p++)
*q++ = deep_copy(*p);
} else val = expresn;
return val;
}
int in_eval_save = 0;
void eval_save() {
push(NIL, stack);
int_during_gc = 0;
in_eval_save = 1;
settype(stack, STACK);
stack->n_car = (NODE *)malloc(sizeof(regs));
if (car(stack) == NULL) {
err_logo(OUT_OF_MEM_UNREC, NIL);
} else {
memcpy(car(stack), ®s, sizeof(regs));
}
in_eval_save = 0;
if (int_during_gc != 0) {
delayed_int();
}
}
void eval_restore() {
int_during_gc = 0;
in_eval_save = 1;
memcpy(®s, car(stack), sizeof(regs));
pop(stack);
in_eval_save = 0;
if (int_during_gc != 0) {
delayed_int();
}
}
/*
#ifdef OBJECTS
NODE *val_eval_driver(NODE *seq) {
val_status = VALUE_OK;
return evaluator(seq, begin_seq);
}
#endif
*/
/* An explicit control evaluator, taken almost directly from SICP, section
* 5.2. list is a flat list of expressions to evaluate. where is a label to
* begin at. Return value depends on where.
*/
NODE *evaluator(NODE *list, enum labels where) {
FIXNUM cont = 0; /* where to go next */
int i;
BOOLEAN tracing = FALSE; /* are we tracing the current procedure? */
inside_evaluator++;
eval_save();
var = var_stack;
newcont(all_done);
newcont(where);
goto fetch_cont;
all_done:
reset_args(var);
eval_restore();
if (dont_fix_ift) {
ift_iff_flag = dont_fix_ift-1;
dont_fix_ift = 0;
}
inside_evaluator--;
return(val);
begin_line:
this_line = list;
val_status = NO_VALUE_OK;
newcont(end_line);
begin_seq:
debprint("begin_seq");
make_tree(list);
if (!is_tree(list)) {
val = UNBOUND;
goto fetch_cont;
}
unev = tree__tree(list);
goto eval_sequence;
end_line:
if (val != UNBOUND) {
if (NOT_THROWING) err_logo(DK_WHAT, val);
}
/* val = NIL; */
goto fetch_cont;
/* ----------------- EVAL ---------------------------------- */
/* Get here for actual argument, from eval_sequence (non-tail), or
from tail call. */
tail_eval_dispatch:
tailcall = 1;
eval_dispatch:
debprint("eval_dispatch");
switch (nodetype(expresn)) {
case QUOTE: /* quoted literal */
val = /* deep_copy */ (node__quote(expresn));
goto fetch_cont;
case COLON: /* variable */
#ifdef OBJECTS
val = varValue(node__colon(expresn));
#else
val = valnode__colon(expresn);
#endif
while (val == UNBOUND && NOT_THROWING)
val = err_logo(NO_VALUE, node__colon(expresn));
goto fetch_cont;
case CONS: /* procedure application */
if (tailcall == 1 && is_macro(car(expresn)) &&
(is_list(procnode__caseobj(car(expresn)))
|| isName(car(expresn), Name_goto))) {
/* tail call to user-defined macro must be treated as non-tail
* because the expression returned by the macro
* remains to be evaluated in the caller's context */
unev = NIL;
goto non_tail_eval;
}
fun = car(expresn);
if (fun == Not_Enough_Node) {
err_logo(TOO_MUCH, NIL); /* When does this happen? */
val = UNBOUND;
goto fetch_cont;
}
if (flag__caseobj(fun, PROC_SPECFORM)) {
argl = cdr(expresn);
goto apply_dispatch;
}
if (cdr(expresn) != NIL)
goto ev_application;
else
goto ev_no_args;
case ARRAY: /* array must be copied */
val = deep_copy(expresn);
goto fetch_cont;
default:
val = expresn; /* self-evaluating */
goto fetch_cont;
}
ev_no_args:
/* Evaluate an application of a procedure with no arguments. */
argl = NIL;
goto apply_dispatch; /* apply the procedure */
ev_application:
/* Evaluate an application of a procedure with arguments. */
unev = cdr(expresn);
argl = NIL;
eval_arg_loop:
debprint("eval_arg_loop");
if (unev == NIL) goto eval_args_done;
expresn = car(unev);
if (expresn == Not_Enough_Node) {
if (NOT_THROWING)
err_logo(NOT_ENOUGH, NIL);
goto eval_args_done;
}
arg_from_macro:
if (nodetype(expresn) != CONS) { /* Don't bother saving registers */
newcont(after_const_arg); /* if the expresn isn't a proc call */
goto eval_dispatch;
}
eval_save();
save(current_unode);
var = var_stack;
tailcall = -1;
didnt_output_name = NIL;
didnt_get_output = cons_list(0, fun, ufun, this_line, END_OF_LIST);
val_status = VALUE_OK;
/* in case of apply or catch */
newcont(accumulate_arg);
goto eval_dispatch; /* evaluate the current argument */
accumulate_arg:
debprint("accumulate_arg");
/* Put the evaluated argument into the argl list. */
reset_args(var);
restore(current_unode);
last_call = fun;
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
}
}
if (stopping_flag == OUTPUT || STOPPING) {
didnt_output_name = NIL;
err_logo(DIDNT_OUTPUT, fun);
}
while (NOT_THROWING && val == UNBOUND) {
val = err_logo(DIDNT_OUTPUT, NIL);
}
eval_restore();
if (stopping_flag == MACRO_RETURN) {
if (val == NIL || val == UNBOUND || cdr(val) != NIL) {
if (NOT_THROWING) {
if (tree_dk_how != NIL && tree_dk_how != UNBOUND)
err_logo(DK_HOW_UNREC, tree_dk_how);
else
err_logo((val!=NIL && val!=UNBOUND) ?
RUNNABLE_ARG : ERR_MACRO, val);
}
goto eval_args_done;
}
expresn = car(val);
stopping_flag = RUN;
goto arg_from_macro;
}
after_const_arg:
if (stopping_flag == THROWING) goto eval_args_done;
push(val, argl);
pop(unev);
goto eval_arg_loop;
eval_args_done:
if (stopping_flag == THROWING) {
val = UNBOUND;
goto fetch_cont;
}
argl = reverse(argl);
/* --------------------- APPLY ---------------------------- */
apply_dispatch:
debprint("apply_dispatch");
/* Load in the procedure's definition and decide whether it's a compound
* procedure or a primitive procedure.
*/
#ifdef OBJECTS
extern NODE* procValueWithParent(NODE*, NODE*, NODE**);
NODE* parent = (NODE*)0;
if (NIL == usual_parent)
usual_parent = current_object;
#ifdef DEB_USUAL_PARENT
dbUsual("evalProcValue BEFORE");
#endif
proc = procValueWithParent(fun,
usual_parent,
&parent);
if (proc != UNDEFINED && parent != 0){
usual_parent = parent;
#ifdef DEB_USUAL_PARENT
dbUsual("evalProcValue AFTER");
#endif
}
#else
proc = procnode__caseobj(fun);
#endif
if (is_macro(fun)) {
num2save(val_status,tailcall);
save2(didnt_get_output,current_unode);
didnt_get_output = the_generation; /* (cons nil nil) */
/* We want a value, but not as actual arg */
newcont(macro_return);
}
#ifdef OBJECTS
if (proc == UNDEFINED) { /* for usual.foo support */
/* The function(fun) may be of the form usual.foo, in which case
* "usual." should be stripped away, and "foo" should be
* resolved in the parent(s) of the current object
*/
extern NODE *parent_list(NODE *obj);
NODE *string = cnv_node_to_strnode(fun);
// first rule out all words shorter than 8 chars
if (getstrlen(string) > 6) {
// check to see if name begins with "usual."
if (!low_strncmp(getstrptr(string), "usual.", 6)){
usual_caller = current_object;
NODE* parent = NIL;
#ifdef DEB_USUAL_PARENT
dbUsual("eval BEFORE");
#endif
proc = getInheritedProcWithParentList(intern(
make_strnode(getstrptr(string) + 6,
getstrhead(string),
getstrlen(string) - 6,
nodetype(string),
strnzcpy)),
usual_parent,
&parent);
// if a proc was found, usual_parent needs to be updated
// to avoid infinite loops when usual is called multiple times
if (proc != UNDEFINED) {
usual_parent = parent;
#ifdef DEB_USUAL_PARENT
dbUsual("eval AFTER");
#endif
}
}
}
}
#endif
if (proc == UNDEFINED) { /* 5.0 punctuationless variables */
if (!varTrue(AllowGetSet)) { /* No getter/setter allowed, punt */
val = err_logo(DK_HOW, fun);
goto fetch_cont;
} else if (argl == NIL) { /* possible var getter */
val = valnode__caseobj(fun);
if (val == UNBOUND && NOT_THROWING)
val = err_logo(DK_HOW, fun);
else if (val != UNBOUND) {
(void)ldefine(cons(fun, cons(
cons(NIL,cons(cons(theName(Name_output),
cons(make_colon(fun),NIL)),
NIL)),
NIL))); /* make real proc so no disk load next time */
setflag__caseobj(fun,PROC_BURIED);
}
goto fetch_cont;
} else { /* var setter */
NODE *name = intern(bf3(fun));
if (valnode__caseobj(name) == UNBOUND &&
!(flag__caseobj(name, (HAS_GLOBAL_VALUE|IS_LOCAL_VALUE)))) {
val = err_logo(DK_HOW, fun);
goto fetch_cont;
}
(void)ldefine(cons(fun, cons(
cons(Listvalue,
cons(cons(Make,
cons(make_quote(bf3(fun)),
cons(Dotsvalue,NIL))),
NIL))
,NIL)));
setflag__caseobj(fun,PROC_BURIED);
argl = cons(bf3(fun), argl);
if (NOT_THROWING)
val = lmake(argl);
goto fetch_cont;
}
}
if (is_list(proc)) goto compound_apply;
/* primitive_apply */
debprint("primitive_apply");
if (NOT_THROWING) {
if ((tracing = flag__caseobj(fun, PROC_TRACED))) {
for (i = 0; i < trace_level; i++) {
print_space(stdout);
}
ndprintf(stdout, "( %s ", fun);
if (argl != NIL) {
arg = argl;
while (arg != NIL) {
print_node(stdout, maybe_quote(car(arg)));
print_space(stdout);
arg = cdr(arg);
}
}
print_char(stdout, ')');
new_line(stdout);
}
val = (*getprimfun(proc))(argl);
if (tracing && NOT_THROWING) {
for (i = 0; i < trace_level; i++) {
print_space(stdout);
}
print_node(stdout, fun);
if (val == UNBOUND)
ndprintf(stdout, " %t\n", message_texts[TRACE_STOPS]);
else {
ndprintf(stdout, " %t %s\n", message_texts[TRACE_OUTPUTS],
maybe_quote(val));
}
}
} else
val = UNBOUND;
/* falls into fetch_cont */
#if DEB_CONT
#define do_case(x) case x: debprint("Fetch_cont = " #x); goto x;
#else
#define do_case(x) case x: goto x;
#endif
fetch_cont:
{
#ifdef USE_GCC_DISPATCH
#define do_label(x) &&x,
static void *dispatch[] = {
do_list(do_label)
0
};
#endif
enum labels x = (enum labels)cont;
cont = (FIXNUM)car(numstack);
numstack=cdr(numstack);
#ifdef USE_GCC_DISPATCH
if (x >= NUM_TOKENS)
abort();
goto *dispatch[x];
#else
switch (x) {
do_list(do_case)
default: abort();
}
#endif
}
/* ----------------- COMPOUND_APPLY ---------------------------------- */
compound_apply:
debprint("compound_apply");
#ifdef mac
check_mac_stop();
#endif
#ifdef ibm
check_ibm_stop();
#endif
#ifdef HAVE_WX
check_wx_stop(0);
#endif
if ((tracing = flag__caseobj(fun, PROC_TRACED))) {
for (i = 0; i < trace_level; i++) print_space(writestream);
trace_level++;
ndprintf(writestream, "( %s ", fun);
}
/* Bind the actuals to the formals */
lambda_apply:
vsp = var_stack; /* remember where we came in */
for (formals = formals__procnode(proc);
formals != NIL;
formals = cdr(formals)) {
parm = car(formals);
if (nodetype(parm) == INT) break; /* default # args */
if (argl != NIL) {
arg = car(argl);
if (tracing) {
print_node(writestream, maybe_quote(arg));
print_space(writestream);
}
} else
arg = UNBOUND;
if (nodetype(parm) == CASEOBJ) {
if (not_local(parm,vsp)) {
push(parm, var_stack);
if (flag__caseobj(parm, IS_LOCAL_VALUE))
settype(var_stack, LOCALSAVE);
var_stack->n_obj = valnode__caseobj(parm);
setflag__caseobj(parm, IS_LOCAL_VALUE);
}
tell_shadow(parm);
setvalnode__caseobj(parm, arg);
if (arg == UNBOUND)
err_logo(NOT_ENOUGH, fun);
} else if (nodetype(parm) == CONS) {
/* parm is optional or rest */
if (not_local(car(parm),vsp)) {
push(car(parm), var_stack);
if (flag__caseobj(car(parm), IS_LOCAL_VALUE))
settype(var_stack, LOCALSAVE);
var_stack->n_obj = valnode__caseobj(car(parm));
setflag__caseobj(car(parm), IS_LOCAL_VALUE);
}
tell_shadow(car(parm));
if (cdr(parm) == NIL) { /* parm is rest */
setvalnode__caseobj(car(parm), argl);
if (tracing) {
if (argl != NIL) pop(argl);
while (argl != NIL) {
arg = car(argl);
print_node(writestream, maybe_quote(arg));
print_space(writestream);
pop(argl);
}
} else argl = NIL;
break;
}
if (arg == UNBOUND) { /* use default */
eval_save();
save(current_unode);
var = var_stack;
tailcall = -1;
list = cdr(parm);
didnt_get_output = cons_list(0, fun, ufun,
list, END_OF_LIST);
didnt_output_name = NIL;
if (NOT_THROWING)
make_tree(list);
else
list = NIL;
if (!is_tree(list)) {
val = UNBOUND;
goto set_args_continue;
}
unev = tree__tree(list);
val = UNBOUND;
expresn = car(unev);
pop(unev);
if (unev != NIL) {
err_logo(BAD_DEFAULT, parm);
val = UNBOUND;
goto set_args_continue;
}
newcont(set_args_continue);
goto eval_dispatch;
set_args_continue:
if (stopping_flag == MACRO_RETURN) {
if (val == NIL || val == UNBOUND || cdr(val) != NIL) {
if (NOT_THROWING)
err_logo((val!=NIL && val!=UNBOUND) ?
RUNNABLE_ARG : ERR_MACRO, val);
} else {
reset_args(var);
expresn = car(val);
stopping_flag = RUN;
didnt_get_output = cons_list(0, fun, ufun,
list, END_OF_LIST);
didnt_output_name = NIL;
tailcall = -1;
newcont(set_args_continue);
goto eval_dispatch;
}
}
restore(current_unode);
last_call = fun;
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
}
}
if (stopping_flag == OUTPUT || STOPPING) {
didnt_output_name = NIL;
err_logo(DIDNT_OUTPUT, fun);
}
while (NOT_THROWING && val == UNBOUND) {
val = err_logo(DIDNT_OUTPUT, NIL);
}
reset_args(var);
eval_restore();
parm = car(formals);
if (stopping_flag == THROWING) {
val = UNBOUND;
goto fetch_cont;
}
arg = val;
}
setvalnode__caseobj(car(parm), arg);
}
if (argl != NIL) pop(argl);
}
if (argl != NIL) {
err_logo(TOO_MUCH, fun);
}
if (check_throwing) {
val = UNBOUND;
goto fetch_cont;
}
vsp = NIL;
if ((tracing = !is_list(fun) && flag__caseobj(fun, PROC_TRACED))) {
if (NOT_THROWING) print_char(writestream, ')');
new_line(writestream);
save(fun);
newcont(compound_apply_continue);
}
last_ufun = ufun;
if (!is_list(fun)) ufun = fun;
last_line = this_line;
this_line = NIL;
/* proc = (is_list(fun) ? anonymous_function(fun) : procnode__caseobj(fun)); */
/* If that's uncommented, begin_apply must get proc from fun, not expresn */
list = bodylist__procnode(proc); /* get the body ... */
make_tree_from_body(list);
if (!is_tree(list) || treepair__tree(list)==NIL) {
val = UNBOUND;
goto fetch_cont;
}
debprint("treeified body");
/* printf("list = 0x%x = ",list); dbprint(list); */
unev = tree__tree(list);
if (NOT_THROWING) stopping_flag = RUN;
output_node = UNBOUND;
if (didnt_get_output == UNBOUND)
val_status = NO_VALUE_OK | STOP_OK | STOP_TAIL;
else if (didnt_get_output == NIL)
val_status = NO_VALUE_OK | STOP_OK | STOP_TAIL |
OUTPUT_OK | OUTPUT_TAIL;
else val_status = OUTPUT_OK | OUTPUT_TAIL;
if (didnt_output_name == NIL) didnt_output_name = fun;
current_unode = cons(NIL,NIL); /* a marker for this proc call */
/* ----------------- EVAL_SEQUENCE ---------------------------------- */
/* Fall through from proc body, call from start or fsubr argument */
eval_sequence:
debprint("eval_sequence");
/* Evaluate each expression in the sequence.
Most of the complexity is in recognizing tail calls.
*/
if (eval_buttonact != NIL) {
make_tree(eval_buttonact);
if (NOT_THROWING) {
if (is_tree(eval_buttonact)) {
unev = append(tree__tree(eval_buttonact), unev);
eval_buttonact = NIL;
}
}
}
if (!RUNNING) goto fetch_cont;
if (nodetype(unev) == LINE) {
if (the_generation != (generation__line(unev))) {
/* something redefined while we're running */
int linenum = 0;
this_line = tree__tree(bodylist__procnode(proc));
while (this_line != unev) {
/* If redef isn't end of line, don't try to fix,
but don't blow up either. (Maybe not called from here.) */
if (this_line == NULL) goto nofix;
if (nodetype(this_line) == LINE) linenum++;
this_line = cdr(this_line);
}
untreeify_proc(proc);
make_tree_from_body(bodylist__procnode(proc));
unev = tree__tree(bodylist__procnode(proc));
while (--linenum >= 0) {
do pop(unev);
while (unev != NIL && nodetype(unev) != LINE);
}
}
nofix: this_line = unparsed__line(unev);
if (ufun != NIL && flag__caseobj(ufun, PROC_STEPPED)) {
if (tracing) {
int i = 1;
while (i++ < trace_level) print_space(stdout);
}
print_node(stdout, this_line);
(void)reader(stdin, " >>> ");
}
}
expresn = car(unev);
pop(unev);
if (expresn != NIL &&
is_list(expresn) && (is_tailform(procnode__caseobj(car(expresn))))) {
i = (int)getprimpri(procnode__caseobj(car(expresn)));
if (i == OUTPUT_PRIORITY) {
if (cadr(expresn) == Not_Enough_Node) {
err_logo(NOT_ENOUGH,car(expresn));
val = UNBOUND;
goto fetch_cont;
}
didnt_output_name = NIL;
if (val_status & OUTPUT_TAIL) {
didnt_get_output = cons_list(0,car(expresn),ufun,this_line,
END_OF_LIST);
fun = car(expresn);
expresn = cadr(expresn);
val_status = VALUE_OK;
goto tail_eval_dispatch;
} else if (val_status & OUTPUT_OK) {
goto tail_eval_dispatch;
} else if (ufun == NIL) {
err_logo(AT_TOPLEVEL,car(expresn));
val = UNBOUND;
goto fetch_cont;
} else if (val_status & STOP_OK) {
didnt_get_output = cons_list(0,car(expresn),ufun,this_line,
END_OF_LIST);
val_status = VALUE_OK;
expresn = cadr(expresn);
newcont(op_want_stop);
goto eval_dispatch;
op_want_stop:
if (NOT_THROWING) err_logo(DK_WHAT_UP, val);
goto fetch_cont;
} else if (val_status & VALUE_OK) {
/* pr apply [output ?] [3] */
debprint("Op with VALUE_OK");
didnt_output_name = fun;
goto tail_eval_dispatch;
} else {
debprint("Op with none of the above");
goto tail_eval_dispatch;
}
} else if (i == STOP_PRIORITY) {
if (ufun == NIL) {
err_logo(AT_TOPLEVEL,car(expresn));
} else if (val_status & STOP_TAIL) {
} else if (val_status & STOP_OK) {
stopping_flag = STOP;
output_unode = current_unode;
} else if (val_status & OUTPUT_OK) {
if (NOT_THROWING) {
if (didnt_get_output == NIL || didnt_get_output == UNBOUND) {
/* actually can happen: PRINT FOREACH ...
will give didn't output message uplevel */
} else
err_logo(DIDNT_OUTPUT, NIL);
}
} else { /* show runresult [stop] inside a procedure */
didnt_output_name = car(expresn);
if (NOT_THROWING) {
if (didnt_get_output == NIL || didnt_get_output == UNBOUND) {
/* actually can happen: STOP during PAUSE */
err_logo(AT_TOPLEVEL, car(expresn));
} else
err_logo(DIDNT_OUTPUT, NIL);
}
}
val = UNBOUND;
goto fetch_cont;
} else { /* maybeoutput */
debprint("maybeoutput");
if (cadr(expresn) == Not_Enough_Node) {
err_logo(NOT_ENOUGH,car(expresn));
val = UNBOUND;
goto fetch_cont;
}
if (ufun == NIL) {
err_logo(AT_TOPLEVEL,car(expresn));
val = UNBOUND;
goto fetch_cont;
}
if (val_status & OUTPUT_TAIL) {
didnt_output_name = NIL;
if (val_status & STOP_TAIL) {
expresn = cadr(expresn);
didnt_get_output = NIL;
val_status = VALUE_OK | NO_VALUE_OK;
} else {
didnt_get_output = cons_list(0,car(expresn),ufun,
this_line,END_OF_LIST);
expresn = cadr(expresn);
val_status = VALUE_OK;
}
goto tail_eval_dispatch;
} else if (val_status & OUTPUT_OK) {
didnt_output_name = NIL;
if (val_status & STOP_OK) {
didnt_get_output = NIL;
val_status = NO_VALUE_OK | VALUE_OK;
expresn = cadr(expresn);
newcont(after_maybeoutput);
goto eval_dispatch;
after_maybeoutput:
if (val == UNBOUND)
lstop(NIL);
else
loutput(cons(val, NIL));
goto fetch_cont;
} else {
goto eval_dispatch;
}
} else if (val_status & STOP_TAIL) {
expresn = cadr(expresn);
didnt_get_output = UNBOUND;
val_status = NO_VALUE_OK;
goto tail_eval_dispatch;
} else if (val_status & STOP_OK) {
expresn = cadr(expresn);
didnt_get_output = UNBOUND;
val_status = NO_VALUE_OK;
newcont(after_maybeoutput);
goto eval_dispatch;
} else {
goto tail_eval_dispatch;
}
}
}
if (unev == NIL) { /* falling off tail of sequence */
debprint("falling off");
if (val_status & NO_VALUE_OK) {
if (val_status & VALUE_OK) /* from runresult */
didnt_get_output = NIL;
else
didnt_get_output = UNBOUND;
} else if (val_status & VALUE_OK) {
} else if (val_status & OUTPUT_OK) {
next_stop_want_output:
save(didnt_get_output);
didnt_get_output = UNBOUND;
val_status &= ~OUTPUT_TAIL;
newcont(fall_off_want_output);
goto tail_eval_dispatch;
fall_off_want_output:
restore(didnt_get_output);
if (stopping_flag == OUTPUT) {
goto fetch_cont; /* repeat body did output */
}
if (NOT_THROWING && val != UNBOUND) {
/* Don't allow just value expr w/o OUTPUT */
err_logo(DK_WHAT, val);
}
goto fetch_cont;
}
goto tail_eval_dispatch;
}
if (car(unev) != NIL && is_list(car(unev)) && /* next is STOP */
(is_tailform(procnode__caseobj(car(car(unev))))) &&
getprimpri(procnode__caseobj(car(car(unev)))) == STOP_PRIORITY) {
if (val_status & STOP_TAIL) {
didnt_get_output = UNBOUND;
goto tail_eval_dispatch;
} else if (val_status & STOP_OK) {
goto non_tail_eval;
} else if (val_status & OUTPUT_OK) {
goto next_stop_want_output;
} /* else treat as non-tail and the STOP will be caught later */
}
non_tail_eval:
debprint("non_tail_eval");
if (nodetype(expresn) != CONS) { /* Don't bother saving registers */
newcont(after_constant); /* if the expresn isn't a proc call */
goto eval_dispatch;
}
eval_save();
didnt_get_output = UNBOUND; /* tell EVAL we don't want a value */
tailcall = 0;
if (nodetype(expresn) == CONS && is_prim(procnode__caseobj(car(expresn)))) {
newcont(no_reset_args); /* primitive */
} else {
var = var_stack;
newcont(eval_sequence_continue);
}
goto eval_dispatch;
eval_sequence_continue:
reset_args(var);
no_reset_args: /* allows catch "foo [local ...] to work */
eval_restore();
if (dont_fix_ift) {
ift_iff_flag = dont_fix_ift-1;
dont_fix_ift = 0;
}
debprint("eval_sequence_continue");
if (stopping_flag == MACRO_RETURN) {
if (val != NIL && is_list(val) && (isName(car(val), Name_tag)))
unev = cdr(val); /* from goto */
else
unev = append(val, unev);
val = UNBOUND;
stopping_flag = RUN;
if (unev == NIL) goto fetch_cont;
} else {
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
goto fetch_cont;
}
}
}
after_constant:
if (val != UNBOUND && NOT_THROWING) {
err_logo(DK_WHAT, val);
val = UNBOUND;
}
if (NOT_THROWING && unev == NIL) {
goto fetch_cont;
}
goto eval_sequence;
compound_apply_continue:
/* Only get here if tracing */
restore(parm); /* saved from fun */
--trace_level;
if (NOT_THROWING) {
for (i = 0; i < trace_level; i++) print_space(writestream);
print_node(writestream, parm);
if (val == UNBOUND)
ndprintf(writestream, " %t\n", message_texts[TRACE_STOPS]);
else {
ndprintf(writestream, " %t %s\n", message_texts[TRACE_OUTPUTS],
maybe_quote(val));
}
}
goto fetch_cont;
/* --------------------- MACROS ---------------------------- */
macro_return:
restore2(didnt_get_output,current_unode);
num2restore(val_status,tailcall);
debprint("macro_return");
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
}
}
while (!is_list(val) && NOT_THROWING) {
val = err_logo(ERR_MACRO,val);
}
if (NOT_THROWING) {
if (didnt_get_output != UNBOUND)
didnt_output_name = fun;
if (is_cont(val)) {
newcont(cont__cont(val));
val = val__cont(val);
goto fetch_cont;
}
if (tailcall <= 0) {
list = val;
make_tree(list);
if (NOT_THROWING) {
stopping_flag = MACRO_RETURN;
if (!is_tree(list)) val = NIL;
else val = tree__tree(list);
} else val = UNBOUND;
goto fetch_cont;
}
list = val;
goto begin_seq;
}
val = UNBOUND;
goto fetch_cont;
#define RUNRESULT_OUTPUT_LEGAL 0
runresult_continuation:
list = val;
#if RUNRESULT_OUTPUT_LEGAL
val_status |= VALUE_OK | NO_VALUE_OK;
val_status &= ~(STOP_TAIL | OUTPUT_TAIL);
#else
val_status = VALUE_OK | NO_VALUE_OK | OUTPUT_OK | STOP_OK;
/* output and stop are not okay, but we give our own err message */
#endif
save(current_unode);
newcont(runresult_followup);
goto begin_seq;
runresult_followup:
restore(current_unode);
debprint("runresult_followup");
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
}
}
if (STOPPING || stopping_flag == OUTPUT)
err_logo(RUNRES_STOP, NIL);
if (val == UNBOUND) {
val = NIL;
} else {
val = cons(val, NIL);
}
goto fetch_cont;
repeat_continuation:
list = cdr(val);
num2save(repcount,user_repcount);
repcount = getint(car(val));
user_repcount = 0;
repeat_again:
val = UNBOUND;
if (repcount == 0) {
repeat_done:
num2restore(repcount,user_repcount);
goto fetch_cont;
}
user_repcount++;
save2(list,var);
var = var_stack;
num2save(repcount,user_repcount);
num2save(val_status,tailcall);
val_status &= ~(VALUE_OK|OUTPUT_TAIL|STOP_TAIL);
if (tailcall == 0) val_status |= NO_VALUE_OK; /* embedded repeat */
newcont(repeat_followup);
goto begin_seq;
repeat_followup:
if (val != UNBOUND && NOT_THROWING) {
err_logo(DK_WHAT, val);
}
num2restore(val_status,tailcall);
num2restore(repcount,user_repcount);
reset_args(var);
restore2(list,var);
if (current_unode != output_unode) {
debprint("rep_foll tailcall");
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
goto repeat_done;
}
}
if (repcount > 0) /* negative means forever */
--repcount;
#ifdef mac
check_mac_stop();
#endif
#ifdef ibm
check_ibm_stop();
#endif
if (RUNNING) goto repeat_again;
val = UNBOUND;
goto repeat_done;
catch_continuation:
list = cdr(val);
catch_tag = car(val);
if (isName(catch_tag, Name_error)) {
push(Erract, var_stack);
if (flag__caseobj(Erract, IS_LOCAL_VALUE))
settype(var_stack, LOCALSAVE);
var_stack->n_obj = valnode__caseobj(Erract);
setflag__caseobj(Erract, IS_LOCAL_VALUE);
setvalnode__caseobj(Erract, UNBOUND);
}
save2(didnt_output_name,didnt_get_output);
num2save(val_status,tailcall);
save2(current_unode,catch_tag);
newcont(catch_followup);
val_status &= ~(STOP_TAIL | OUTPUT_TAIL);
goto begin_seq;
catch_followup:
restore2(current_unode,catch_tag);
num2restore(val_status,tailcall);
restore2(didnt_output_name,didnt_get_output);
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
goto fetch_cont;
}
}
if (NOT_THROWING && val != UNBOUND && !(val_status & VALUE_OK))
err_logo(DK_WHAT, val);
if (stopping_flag == THROWING &&
((compare_node(throw_node, catch_tag, TRUE) == 0) ||
(isName(throw_node, Name_error) && isName(catch_tag, Name_error)))) {
throw_node = UNBOUND;
stopping_flag = RUN;
val = output_node;
}
goto fetch_cont;
#ifdef OBJECTS
withobject_continuation:
save2(didnt_output_name,didnt_get_output);
num2save(val_status,tailcall);
save2(current_unode,current_object);
#ifdef DEB_USUAL_PARENT
dbUsual("withObjectCont");
#endif
newcont(withobject_followup);
current_object = car(val);
usual_parent = current_object;
newcont(cont__cont(cdr(val)));
list = val = val__cont(cdr(val));
val_status &= ~(STOP_TAIL | OUTPUT_TAIL);
goto fetch_cont;
withobject_followup:
restore2(current_unode,current_object);
num2restore(val_status,tailcall);
restore2(didnt_output_name,didnt_get_output);
usual_parent = current_object;
#ifdef DEB_USUAL_PARENT
dbUsual("withObjectFollow");
#endif
if (current_unode != output_unode) {
if (STOPPING || RUNNING) output_node = UNBOUND;
if (stopping_flag == OUTPUT || STOPPING) {
stopping_flag = RUN;
val = output_node;
goto fetch_cont;
}
}
if (NOT_THROWING && val != UNBOUND && !(val_status & VALUE_OK))
err_logo(DK_WHAT, val);
goto fetch_cont;
#endif /* OBJECTS */
goto_continuation:
if (NOT_THROWING) {
if (ufun == NIL) {
err_logo(AT_TOPLEVEL, theName(Name_goto));
val = UNBOUND;
goto fetch_cont;
}
proc = procnode__caseobj(ufun);
list = bodylist__procnode(proc);
unev = tree__tree(list);
while (unev != NIL && !check_throwing) {
if (nodetype(unev) == LINE)
this_line = unparsed__line(unev);
expresn = car(unev);
pop(unev);
if (is_list (expresn) &&
(isName(car(expresn), Name_tag)) &&
(nodetype(cadr(expresn)) == QUOTE) &&
compare_node(val, node__quote(cadr(expresn)), TRUE) == 0) {
val = cons(theName(Name_tag), unev);
stopping_flag = MACRO_RETURN;
goto fetch_cont;
}
}
err_logo(BAD_DATA_UNREC, val);
}
val = UNBOUND;
goto fetch_cont;
begin_apply:
/* This is for lapply. */
expresn = car(val);
while (nodetype(expresn) == ARRAY && NOT_THROWING)
expresn = err_logo(APPLY_BAD_DATA, expresn);
argl = append(cadr(val), NIL);
val = UNBOUND;
while (!is_list(argl) && NOT_THROWING)
argl = err_logo(APPLY_BAD_DATA, argl);
if (NOT_THROWING && expresn != NIL) {
if (is_list(expresn)) { /* template */
if (is_list(car(expresn)) && cdr(expresn) != NIL) {
if (is_list(cadr(expresn))) {
/* procedure text form [[param ...] [instr ...] ...] */
proc = anonymous_function(expresn);
debprint("anon func");
if (stopping_flag == THROWING) goto fetch_cont;
tracing = 0;
if (tailcall <= 0) {
save(var);
var = var_stack;
newcont(after_lambda);
}
goto lambda_apply;
}
/* lambda form [[param ...] instr ...] */
formals = car(expresn);
if (tailcall <= 0) {
save(var);
var = var_stack;
newcont(after_lambda);
}
/* numsave(tailcall); */
tailcall = 0;
llocal(formals); /* bind the formals locally */
/* numrestore(tailcall); */
for ( ;
formals != NIL && argl != NIL && NOT_THROWING;
formals = cdr(formals),
argl = cdr(argl))
setvalnode__caseobj(car(formals), car(argl));
if (formals != NIL) {
err_logo(NOT_ENOUGH, expresn);
goto fetch_cont;
} else if (argl != NIL) {
err_logo(DK_WHAT, car(argl));
goto fetch_cont;
}
list = cdr(expresn);
goto lambda_qm;
} else { /* question-mark form [instr ...] */
qm_list = argl;
list = expresn;
lambda_qm:
make_tree(list);
if (list == NIL || !is_tree(list)) {
goto fetch_cont;
}
unev = tree__tree(list);
if (tailcall <= 0) {
val_status &= ~(STOP_TAIL | OUTPUT_TAIL);
save(var);
var = var_stack;
newcont(after_lambda);
}
goto eval_sequence;
}
} else { /* name of procedure to apply */
int min, max, n;
NODE *arg;
fun = intern(expresn);
check_library(fun);
proc = procnode__caseobj(fun);
while (proc == UNDEFINED && NOT_THROWING) {
val = err_logo(DK_HOW_UNREC, fun);
}
if (NOT_THROWING) {
if (nodetype(proc) == CONS) {
min = getint(minargs__procnode(proc));
max = getint(maxargs__procnode(proc));
} else {
if (getprimdflt(proc) < 0) { /* special form */
err_logo(DK_HOW_UNREC, fun); /* can't apply */
goto fetch_cont;
} else {
min = getprimmin(proc);
if (min == OK_NO_ARG) min = 0;
max = getprimmax(proc);
}
}
for (n = 0, arg = argl; arg != NIL; n++, arg = cdr(arg));
if (n < min) {
err_logo(NOT_ENOUGH, NIL);
} else if (n > max && max >= 0) {
err_logo(TOO_MUCH, fun);
} else {
if (tailcall <= 0) {
save(var);
var = var_stack;
newcont(after_lambda);
}
goto apply_dispatch;
}
}
}
}
goto fetch_cont;
after_lambda:
reset_args(var);
restore(var);
goto fetch_cont;
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...