/****************************************************************************** UNSW Prolog (version 4) Written by Claude Sammut Department of Computer Science University of New South Wales (and St. Joseph's U., Philadelphia) Copyright (c) 1983 - Claude Sammut ******************************************************************************/ /* Structure sharing Prolog interpreter */ #include "g.h" #include #include #define CALL 0 #define EXIT 1 #define FAILG 2 #define REDO 3 binding *stack; /* the global variable stack */ environment *env_stack; /* the local control stack */ static binding **trail, *frame2; static int STACK_SIZE, TRAIL_SIZE, ENV_SIZE; short sp = 0, tp = 0, parent = -1, env = -1; static short S_BOTTOM = 0, BOTTOM = -1; static char tracing; pval termb; binding *frameb; jmp_buf env3; static short tlevel; char run = FALSE; static char ss = FALSE; extern atom *_cut; extern integer *stack_int; bind(v, f1, term, f2) var *v; pval term; binding *f1, *f2; { register binding *p; #ifdef DEBUG printf("BIND "); run = FALSE; prin(v, 1200, f1); run = TRUE; printf(" TO "); print(term, 1200, f2); #endif p = &(f1[v -> offset]); p -> termv = term; p -> framev = f2; if (f1 < frame2) { #ifdef DEBUG printf("PUT ON TRAIL\n"); #endif if (tp <= TRAIL_SIZE) trail[tp++] = p; else fatal("TRAIL OVERFLOW"); } } isbound(v, f) var *v; binding *f; { register binding *p; p = &(f[v -> offset]); if (p -> termv == 0) return(FALSE); termb = p -> termv; frameb = p -> framev; return(TRUE); } static long n_unify = 0; unify(term1, frame1, term2, frame2) register pval term1, term2; binding *frame1, *frame2; { register int i; #ifdef DEBUG run = FALSE; printf("***********\n"); prin(term1,1200 , frame1); printf(" %d\n", frame1 - stack); prin(term2, 1200, frame2); printf(" %d\n", frame2 - stack); printf("===========\n"); run = TRUE; #endif n_unify++; L1: if (term1 == term2 && frame1 == frame2) return(TRUE); switch (TYPE(term1)) { case ATOM: case STRING: case PREDEF: if (isatom(term2)) return(term1 == term2); else break; case INT: if (isinteger(term2)) return((term1 == (pval) stack_int ? (int) frame1 : term1 -> i.int_val) == (term2 == (pval) stack_int ? (int) frame2 : term2 -> i.int_val)); else break; case VAR: if (isbound(term1, frame1)) { term1 = termb; frame1 = frameb; goto L1; } else if (isvariable(term2)) if (isbound(term2, frame2)) bind(term1, frame1, termb, frameb); else if (frame2 > frame1) bind(term2, frame2, term1, frame1); else bind(term1, frame1, term2, frame2); else bind(term1, frame1, term2, frame2); return(TRUE); case FN: if (iscompound(term2)) { #ifdef PRINC_VAR /* unifies principal term as well as args */ if (SIZE(term1) != SIZE(term2)) return(FALSE); for (i = 0; i <= SIZE(term1); i++) if (! unify(term1 -> c.term[i], frame1, term2 -> c.term[i], frame2)) return(FALSE); return(TRUE); #else if (term1 -> c.term[0] != term2 -> c.term[0]) return(FALSE); if (SIZE(term1) != SIZE(term2)) return(FALSE); for (i = 1; i <= SIZE(term1); i++) if (! unify(term1 -> c.term[i], frame1, term2 -> c.term[i], frame2)) return(FALSE); return(TRUE); #endif } else break; case LIST: if (islist(term2)) if (unify(term1->c.term[0], frame1, term2->c.term[0], frame2)) { term1 = term1 -> c.term[1]; term2 = term2 -> c.term[1]; goto L1; } else return(FALSE); else break; } if (isvariable(term2)) { if (isbound(term2, frame2)) { term2 = termb; frame2 = frameb; goto L1; } else bind(term2, frame2, term1, frame1); return(TRUE); } else return(FALSE); } clear_frame(n) register int n; { #ifdef DEBUG printf("CLEAR %d STARTING AT %d\n", n, sp); #endif if ((sp + n) >= STACK_SIZE) fatal("STACK OVERFLOW"); while (n-- != 0) {stack[sp].termv = 0; stack[sp++].framev = 0;} } unbind(v, f) var *v; binding *f; { termb = (pval) v; frameb = f; while (isvariable(termb) && isbound(termb, frameb)); } #define MAXARGS 16 static eval(a, t, frame1) register pval a; compterm *t; binding *frame1; { register int i, nargs; pval arg[MAXARGS]; binding *frame[MAXARGS]; nargs = a -> p.nargs; if(t -> size != nargs && nargs != NPRED) { warning("Incorrect number of arguments to built-in"); return(FALSE); } if (t -> size > MAXARGS) { warning("TOO MANY ARGUMENTS FOR BUILT IN PREDICATE"); return(FALSE); } for (i = 1; i <= t -> size; i++) if (isvariable(t -> term[i])) { unbind(t -> term[i], frame1); arg[i-1] = termb; frame[i-1] = frameb; } else { arg[i-1] = t -> term[i]; frame[i-1] = frame1; } if (nargs == NPRED) return FVAL(a)(arg, frame, t -> size); else return FVAL(a)(arg, frame); } static long n_calls = 0; static int successful = FALSE; static lush(c, argn, print_vars) clause *c; int argn; int print_vars; { register environment *p; register pval t; short old_sp, old_tp, base; binding *frame1; register clause *clist; pval a, *cl; int kind = CALL; #ifdef DEBUG printf("LUSH\n"); parent = env = -1; sp = tp = 0; #endif old_tp = tp; frame2 = &(stack[sp]); cl = &(c -> goal[1]); clear_frame(argn); NEW_CLAUSE: #ifdef DEBUG printf("GO NEW_CLAUSE\n"); #endif frame1 = frame2; parent = env; base = sp; NEW_GOAL: #ifdef DEBUG printf("NEW GOAL\n"); #endif if (*cl == 0) goto SUCCEED; n_calls++; t = *cl; if (isvariable(t)) { unbind(t, frame1); t = (pval) termb; frame1 = frameb; #ifdef DEBUG printf("\nFrame1 = %o; offset = %d\n", frame1, t -> v.offset); print(t, 1200, frame1); #endif } if (t == (pval) _cut) { #ifdef DEBUG printf("CUT %d %d\n", parent, env); #endif env = parent; cl++; env_stack[env].clist = 0; goto NEW_GOAL; } switch (TYPE(t)) { case ATOM: if (clist = t -> a.val) tracing = t -> a.traced; else goto FAIL; break; case PREDEF: if (t -> p.traced) trace_print(t, frame1, CALL); if ((*(t -> p.fn))()) { cl++; old_tp = tp; goto NEW_GOAL; } else goto FAIL; case FN: a = t -> c.term[0]; #ifdef PRINC_VAR if (isvariable(a)) { unbind(a, frame1); if (isatom(termb)) a = termb; else { fprintf(stderr, "Principal term must be an atom: "); run = FALSE; print(termb, 1200, frameb); run = TRUE; goto FAIL; } } #endif if (TYPE(a) == PREDEF) { #ifdef DEBUG printf("*** "); print(t, 1200, frame1); #endif if (a -> p.traced) trace_print(t, frame1, CALL); if (eval(a, t, frame1)) { cl++; old_tp = tp; goto NEW_GOAL; } else goto FAIL; } else if (clist = a -> a.val) tracing = a -> a.traced; else goto FAIL; break; default: warning("Cannot execute goal"); print(t, 1200, frame1); return; } BACKTRACK_POINT: #ifdef DEBUG printf("BACKTRACK POINT\n"); #endif frame2 = &(stack[sp]); old_sp = sp; ALTERNATIVE: #ifdef DEBUG printf("ALTERNATIVE\n"); print(t, 1200, frame1); #endif if (clist == 0) goto FAIL; clear_frame(clist -> nvars); if (unify(clist -> goal[0], frame2, t, frame1)) { if (tracing) { trace_print(clist -> goal[0], frame2, kind); kind = CALL; } if (++env == ENV_SIZE) fatal("ENVIRONMENT STACK FULL"); #ifdef DEBUG printf("MAKING NEW ENVIRONMENT AT %d\n", env); #endif p = &(env_stack[env]); p -> cl = cl; p -> tracing = tracing ? tlevel : 0; p -> sp = old_sp; p -> clist = clist -> rest; p -> parent = parent; p -> tp = old_tp; old_tp = tp; #ifdef DEBUG1 dump_stack(); dump_env(); #endif cl = &(clist -> goal[1]); goto NEW_CLAUSE; } else { sp = old_sp; #ifdef DEBUG printf("TRAIL is %d, old TRAIL is %d\n", tp, old_tp); print(t, 1200, frame1); #endif while (tp > old_tp) trail[--tp] -> termv = 0; clist = clist -> rest; goto ALTERNATIVE; } SUCCEED: #ifdef DEBUG printf("SUCCESS %d\n", parent); #endif if (parent > BOTTOM) { #ifdef DEBUG printf("POP %d\n", parent); #endif p = &(env_stack[parent]); parent = p -> parent; if (parent == BOTTOM) base = S_BOTTOM; else base = env_stack[parent].sp; frame1 = &stack[base]; if (tracing = p -> tracing) { tlevel = tracing - 1; trace_print(*(p -> cl), frame1, EXIT); --tlevel; } cl = (p -> cl) + 1; goto NEW_GOAL; } else if (print_vars) prvars(argn); else return; FAIL: #ifdef DEBUG printf("BACKTRACKING %d\n", env); #endif if (tracing) trace_print(t, frame1, FAILG); if (env < 0) { if (! successful && print_vars) printf("** no\n"); return; } p = &(env_stack[env--]); cl = p -> cl; sp = p -> sp; clist = p -> clist; parent = p -> parent; if (parent == -1) base = 0; else base = env_stack[parent].sp; old_tp = p -> tp; frame1 = &(stack[base]); t = *cl; while (tp > old_tp) trail[--tp] -> termv = 0; if (tracing = p -> tracing) { tlevel = tracing - 1; kind = REDO; } goto BACKTRACK_POINT; } static trace_print(cl, frame2, kind) clause *cl; binding *frame2; int kind; { extern FILE *output; int i; static char trace_type[] = {'C', 'E', 'F', 'R'}; static char arrow_head[] = {'>', '<', '<', '>'}; if (tlevel < 80) ++tlevel; putc(trace_type[kind], output); for (i = tlevel; i != 0; i--) putc('|', output); putc(arrow_head[kind], output); print(cl, 1200, frame2); } static prvars(argn) int argn; { register i; extern var **varcell; extern atom *anon; extern FILE *output; FILE *old_output; successful = TRUE; old_output = output; output = stdout; if (argn) { putchar('\n'); for (i = 0; i != argn; i++) { if (varcell[i] -> pname == anon) continue; printf("%s = ", varcell[i] -> pname -> name); print(varcell[i], 1200, &(stack[S_BOTTOM])); } } else printf("** yes\n"); output = old_output; } dump_stack() { extern FILE *output; register i, j; run = FALSE; fprintf(output, "\n------------- VARIABLE STACK -------------\n"); for (i = sp - 1; i >= 0; i--) { fprintf(output, "%3d :", i); if (stack[i].termv == (pval) stack_int) fprintf(output, " %d\n", (int) stack[i].framev); else { if ((j = (int) (stack[i].framev - stack)) || TYPE(stack[i].termv) == VAR) fprintf(output, " %-3d ", j); else fprintf(output, " "); print(stack[i].termv, 1200 , stack[i].framev); } } run = TRUE; } dump_env() { extern FILE *output; register i, base; ss = TRUE; fprintf(output, "\n------------- ENVIRONMENT STACK ------------\n"); for (i = env; i != -1; i--) { if (env_stack[i].parent == BOTTOM) base = S_BOTTOM; else base = env_stack[env_stack[i].parent].sp; #ifdef DEBUG fprintf(output, "%3d : %4d %4d %4d %4d ", i, env_stack[i].parent, base, env_stack[i].sp, env_stack[i].tp ); #else fprintf(output, "%3d : %4d %4d ", i, env_stack[i].parent, base ); #endif print(*(env_stack[i].cl), 1200, &stack[base]); /* fprintf(output, " %d ",&(stack[env_stack[i].sp])); */ } ss = FALSE; fprintf(output, "============================================\n"); } struct tbuffer { long utime; long stime; long cutime; long cstime; }; int do_timing = FALSE; execute(c, argn, print_vars) clause *c; int argn; int print_vars; { extern FILE *input, *piport, *output, *poport; extern atom *init_prompt, *read_prompt, *prompt_string; extern long times(); struct tbuffer buffer[2]; FILE *old_input, *old_output; register int i; double total; short O_BOTTOM = BOTTOM, O_S_BOTTOM = S_BOTTOM, O_parent = parent, O_tp = tp; binding *O_frame2 = frame2; #ifdef DEBUG printf("BEGIN EXECUTION\n"); #endif BOTTOM = env; S_BOTTOM = sp; #ifdef DEBUG fprintf(stderr, "BOTTOM = %d, sp = %d\n", BOTTOM, sp); #endif old_input = input; input = piport; old_output = output; output = poport; prompt_string = read_prompt; successful = FALSE; run = TRUE; tlevel = 0; n_calls = n_unify = 0; if (do_timing) { times(&(buffer[0])); for (i = 0; i < 100 && do_timing; i++) { if (! setjmp(env3)) lush(c, argn, print_vars); env = BOTTOM; sp = S_BOTTOM; BOTTOM = O_BOTTOM; S_BOTTOM = O_S_BOTTOM; parent = O_parent; tp = O_tp; frame2 = O_frame2; } times(&(buffer[1])); total = (double)(buffer[1].utime - buffer[0].utime) / 60; fprintf(output, "\n%d procedure calls executed in %1.2f seconds\n", n_calls, total ); fprintf(output, "Run executed %d calls to unify\n", n_unify); if (total > 0.0001) fprintf(output, "%1.2f procedure calls/sec\n", (double)(n_calls) / total ); } else if (! setjmp(env3)) lush(c, argn, print_vars); run = FALSE; input = old_input; output = old_output; prompt_string = init_prompt; env = BOTTOM; sp = S_BOTTOM; BOTTOM = O_BOTTOM; S_BOTTOM = O_S_BOTTOM; parent = O_parent; tp = O_tp; frame2 = O_frame2; #ifdef DEBUG fprintf(stderr, "BOTTOM = %d, sp = %d\n", BOTTOM, sp); printf("END EXECUTION\n"); #endif } set_stacks(n) int n; { extern char *calloc(); STACK_SIZE = n; ENV_SIZE = n/3; TRAIL_SIZE = n/4; stack = (binding *) calloc(STACK_SIZE, sizeof(binding)); env_stack = (environment *) calloc(ENV_SIZE, sizeof(environment)); trail = (binding **) calloc(TRAIL_SIZE, sizeof(binding *)); if (stack == 0 || env_stack == 0 || trail == 0) { fprintf(stderr, "Not enough memory for Prolog\n"); exit(); } }