////////////////////////////////////////////////////////////////////////////// //Copyright 2008 // Andrew Gacek, Steven Holte, Gopalan Nadathur, Xiaochu Qi, Zach Snow ////////////////////////////////////////////////////////////////////////////// // This file is part of Teyjus. // // // // Teyjus 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. // // // // Teyjus 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 Teyjus. If not, see . // ////////////////////////////////////////////////////////////////////////////// /****************************************************************************/ /* */ /* File hnorm.c. */ /* This file contains the head normalization routines. */ /* These procedures are based on the suspension calculus, and the reduction */ /* strategy with lazy reduction, lazy substitution and lazy heap */ /* commitment is chosen. A SML realization of this process is described in */ /* paper "Choices in Representation and Reduction Strategies for Lambda */ /* Terms in Intersional Contexts". */ /****************************************************************************/ #ifndef HNORM_C #define HNORM_C #include #include "dataformats.h" #include "mctypes.h" #include "hnorm.h" #include "hnormlocal.h" #include "abstmachine.h" #include "../system/error.h" //for debugging: to be removed #include #include "printterm.h" #include "../system/stream.h" /*****************************************************************************/ /* a global(to file hnorm.c) encoding of the explicit suspension environment*/ /* and simple checking and updating functions on this environment */ /*****************************************************************************/ /* environment of the implicit suspension, which is initialized to empty*/ static int ol, nl; static DF_EnvPtr envlist; /* clean the environment to empty */ static void HN_setEmptyEnv() { ol = 0; nl = 0; envlist = DF_EMPTY_ENV; } /* set the environment according to given values */ static void HN_setEnv(int o, int n, DF_EnvPtr e) { ol = o; nl = n; envlist = e; } /* is an empty environment? */ static Boolean HN_isEmptyEnv() { return ((ol == 0) && (nl == 0)); } /****************************************************************************/ /* Functions for creating (modifying) the environment list in the suspension*/ /* environment defined by ol, nl and envlist according to their current */ /* values. */ /****************************************************************************/ /* Add n (n > 0) dummy environment items to the front of the current environment list: @(nl+n-1) :: ... :: @nl :: envlist. New dummy env items are created on the current heap top. */ static DF_EnvPtr HN_addNDummyEnv(int n) { int i; DF_EnvPtr last = envlist, current = NULL; AM_heapError(AM_hreg + n * DF_ENV_DUMMY_SIZE); for (i = 0; i < n; i++){ current = (DF_EnvPtr)AM_hreg; DF_mkDummyEnv(AM_hreg, nl+i, last); AM_hreg += DF_ENV_DUMMY_SIZE; last = current; } return current; } /* Add n (n > 0) pair environment items to the front of the current environment list as the following: ([|an,myol,mynl,myenv|],nl):: ... ::([|ai,myol,mynl,myenv|],nl)::envlist, where ai is the ith argument in the vector referred to by argvec. Note if ai is an atomic term, the suspension over it is eagerly evaluated. */ static DF_EnvPtr HN_addNPair(DF_TermPtr argvec, int myol, int mynl, DF_EnvPtr myenv, int n) { int i; DF_EnvPtr last = envlist, current = NULL; MemPtr myEnvlist = AM_hreg; MemPtr newhtop = AM_hreg + n * DF_ENV_PAIR_SIZE; AM_heapError(newhtop); AM_hreg = newhtop; //spare space for n pair env items for (i = 1; i<= n; i++) { current = (DF_EnvPtr)myEnvlist; DF_mkPairEnv(myEnvlist, nl, HNL_suspAsEnv(argvec,myol,mynl,myenv), last); myEnvlist += DF_ENV_PAIR_SIZE; last = current; argvec = (DF_TermPtr)(((MemPtr)argvec) + DF_TM_ATOMIC_SIZE); } return current; } /* A specialized version of HN_addNPair when the incoming environment is empty. Now, n (n > 0) pair environment items are added to the front of the current environment list as the following: (an,0) :: ... :: (a1,0) :: envlist, where ai is the ith argument in the vector referred to by argvec. */ static DF_EnvPtr HN_addNPairEmpEnv(DF_TermPtr argvec, int n) { int i; DF_EnvPtr last = envlist, current = NULL; AM_heapError(AM_hreg + n * DF_ENV_PAIR_SIZE); for (i = 1; i <= n; i++) { current = (DF_EnvPtr)AM_hreg; DF_mkPairEnv(AM_hreg, 0, argvec, last); AM_hreg += DF_ENV_PAIR_SIZE; last = current; argvec = (DF_TermPtr)(((MemPtr)argvec) + DF_TM_ATOMIC_SIZE); } return current; } /****************************************************************************/ /* A function for pushing suspension over n abstractions following the rule */ /* [|lam(n,body), ol, nl, envlist|] */ /* -> lam(n, [|body, ol+n, nl+n, @(nl+n-1) :: ... :: @nl :: envlist |] */ /* The result is committed on the current top of heap. */ /* The top-level (implicit) suspension is given by the global variable */ /* ol, nl, and envlist. */ /* This function is used in HN_hnormSusp, HN_hnormSuspOCC and HN_lnormSusp. */ /****************************************************************************/ static DF_TermPtr HN_pushSuspOverLam(DF_TermPtr lamPtr) { DF_TermPtr rtPtr; //term pointer to be returned DF_TermPtr suspPtr; //explicit susp as the lam body in the result int numabs =DF_lamNumAbs(lamPtr); int newol = ol + numabs, newnl = nl + numabs; MemPtr newhtop = AM_hreg+ DF_TM_SUSP_SIZE+ numabs*DF_TM_ATOMIC_SIZE; DF_EnvPtr newenv; AM_embedError(newol); AM_embedError(newnl); AM_heapError(newhtop); newenv = HN_addNDummyEnv(numabs); suspPtr = HNL_suspAsEnv(DF_lamBody(lamPtr), newol, newnl, newenv); rtPtr = (DF_TermPtr)AM_hreg; //create lam over the susp DF_mkLam(AM_hreg, numabs, suspPtr); AM_hreg = newhtop; return rtPtr; } /****************************************************************************/ /* functions for (weak) head normalizing terms of known categories */ /*--------------------------------------------------------------------------*/ /* General comments: */ /* An implicit suspension is given by the global variables ol, nl and */ /* envlist together with the first argument tmPtr to the sub-functions: */ /* [|tmPtr, ol, nl, envlist|] */ /* The suspension environment could be empty in which case the term */ /* being normalized is tmPtr itself. */ /* The second argument of the sub-functions whnf is a flag indicating */ /* whether a head normal form or a weak head normal form is being */ /* computed. */ /****************************************************************************/ static DF_TermPtr HN_hnormDispatch(DF_TermPtr tmPtr, Boolean whnf); /* (weak) head normalize bound variable or implicit suspension with bound variable as term skeleton. */ static DF_TermPtr HN_hnormBV(DF_TermPtr bvPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned if (HN_isEmptyEnv()){ //[|#i, 0, 0, nil|] -> #i rtPtr = bvPtr; HNL_setRegsRig(bvPtr); } else { //non-empty env int dbind = DF_bvIndex(bvPtr); if (dbind > ol) { //[|#i,ol,nl,e|] -> #i-ol+nl int newind = dbind - ol + nl; AM_embedError(newind); rtPtr =(DF_TermPtr)AM_hreg; HNL_pushBV(newind); HNL_setRegsRig(rtPtr); HN_setEmptyEnv(); } else { // i <= ol DF_EnvPtr envitem = DF_envListNth(envlist, dbind); int nladj = nl-DF_envIndex(envitem); if (DF_isDummyEnv(envitem)){ //[|#i,ol,nl,..@l..|]->#(nl-l) rtPtr = (DF_TermPtr)AM_hreg; HNL_pushBV(nladj); HNL_setRegsRig(rtPtr); HN_setEmptyEnv(); } else { //pair env [|#i,ol,nl,..(s,l)..|] -> [|s,0,(nl-l),nil|] DF_TermPtr tmPtr = DF_termDeref(DF_envPairTerm(envitem)); if ((nladj != 0) && (DF_isSusp(tmPtr))) {//combine susp int newnl = DF_suspNL(tmPtr)+nladj; AM_embedError(newnl); HN_setEnv(DF_suspOL(tmPtr), newnl, DF_suspEnv(tmPtr)); rtPtr = HN_hnormDispatch(DF_suspTermSkel(tmPtr), whnf); } else { HN_setEnv(0, nladj, DF_EMPTY_ENV); rtPtr = HN_hnormDispatch(tmPtr, whnf); } } //pair env } // i<= ol } //non-empty env return rtPtr; } /* (weak) head normalize an abstraction or implicit suspension with term skeleton as an abstraction. If an implicit suspension is weak head normalized, the suspension itself is returned. The descendant of this suspension over its abstraction skeleton is performed in the subsequent app case on a fly. Note that this is the only case that hnorm termniates with a non-empty environment. */ static DF_TermPtr HN_hnormLam(DF_TermPtr lamPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned if (whnf) return rtPtr = lamPtr; //weak hn else { //whnf = FALSE int numabs = DF_lamNumAbs(lamPtr); DF_TermPtr newbody; if (HN_isEmptyEnv()){ newbody = HN_hnormDispatch(DF_lamBody(lamPtr), FALSE); rtPtr = lamPtr; //body must have been adjusted in place } else { // non-empty env //[|lam(n,t),ol,nl,e|] ->lam(n,[|t,ol+n,nl+n,@nl+n-1...::@nl::e|] int newol = ol+numabs, newnl = nl+numabs; AM_embedError(newol); AM_embedError(newnl); HN_setEnv(newol, newnl, HN_addNDummyEnv(numabs)); newbody = HN_hnormDispatch(DF_lamBody(lamPtr), FALSE); /* create a new lam on the result of hn the lam body */ rtPtr = (DF_TermPtr)AM_hreg; HNL_pushLam(newbody, numabs); } // non-empty env AM_numAbs += numabs; } //whnf == FALSE return rtPtr; } /* (weak) head normalize cons or implicit suspension over cons */ static DF_TermPtr HN_hnormCons(DF_TermPtr consPtr, Boolean whnf) { DF_TermPtr argvec = DF_consArgs(consPtr), rtPtr; //term pointer to be returned if (HN_isEmptyEnv()){ AM_argVec = argvec; AM_numArgs = DF_CONS_ARITY; rtPtr = consPtr; } else { Boolean changed = HNL_makeConsArgvec(argvec, ol, nl, envlist); if (changed){ //new argvec is built because of pushing susp rtPtr = (DF_TermPtr)AM_hreg; HNL_pushCons(AM_argVec); } else rtPtr = consPtr; HN_setEmptyEnv(); } HNL_setRegsCons(rtPtr); return rtPtr; } /* (weak) head normalize application or implicit suspension over application. The old application term is destructively changed into a reference to its head normal form or its weak head normal form if the weak heap normal form is not an implicit suspension (in which case the term skeleton must be an abstraction.). */ static DF_TermPtr HN_hnormApp(DF_TermPtr appPtr, Boolean whnf) { DF_TermPtr funPtr = DF_appFunc(appPtr), argvec = DF_appArgs(appPtr), rtPtr; // term pointer to be returned DF_TermPtr oldFunPtr = funPtr; int arity = DF_appArity(appPtr); Boolean emptyTopEnv = HN_isEmptyEnv(); int myol, mynl; //for book keeping the implicit suspension env DF_EnvPtr myenvlist; //for book keeping the implicit suspension env int myarity = arity; //book keeping the arity before contraction if (!emptyTopEnv) { //book keeping the current environment myol = ol; mynl = nl; myenvlist = envlist; } funPtr = HN_hnormDispatch(funPtr, TRUE); //whf of the function while ((arity > 0) && (DF_isLam(funPtr))) { //perform contraction on top-level redexes while you can DF_TermPtr lamBody = DF_lamBody(funPtr); //abs body int numAbsInFun = DF_lamNumAbs(funPtr); int numContract = ((arity<=numAbsInFun) ? arity : numAbsInFun); DF_EnvPtr newenv; int newol = ol + numContract; AM_embedError(newol); if (emptyTopEnv) newenv = HN_addNPairEmpEnv(argvec, numContract); else newenv = HN_addNPair(argvec, myol, mynl, myenvlist, numContract); HN_setEnv(newol, nl, newenv); if (arity == numAbsInFun){ funPtr = HN_hnormDispatch(lamBody, whnf); arity = 0; } else if (arity > numAbsInFun) { funPtr = HN_hnormDispatch(lamBody, TRUE); argvec=(DF_TermPtr)(((MemPtr)argvec)+numAbsInFun*DF_TM_ATOMIC_SIZE); arity -= numAbsInFun; } else { //arity < numabsInFun DF_TermPtr newBody = (DF_TermPtr)AM_hreg; HNL_pushLam(lamBody, (numAbsInFun-arity)); funPtr = HN_hnormDispatch(newBody, whnf); arity = 0; } }// while ((arity >0) && (DF_IsLam(fun))) //update or create application if (arity == 0) { //app disappears rtPtr = funPtr; if (emptyTopEnv && HN_isEmptyEnv()) HNL_updateToRef(appPtr, funPtr); } else { //app persists; Note: now HN_isEmptyEnv must be TRUE Boolean changed; if (emptyTopEnv) changed = HNL_makeArgvecEmpEnv(argvec, arity); else changed = HNL_makeArgvec(argvec,arity,myol,mynl,myenvlist); if ((!changed) && (arity == myarity) && (funPtr == oldFunPtr)) { rtPtr = appPtr; } else {// create new app and in place update the old if empty top env rtPtr = (DF_TermPtr)AM_hreg; HNL_pushApp(AM_head, AM_argVec, AM_numArgs); if (emptyTopEnv) HNL_updateToRef(appPtr, rtPtr); } } return rtPtr; } /* (weak) head normalize (explicit) suspension or implicit suspension with a suspension term skeletion. The explicit suspension is destructivly changed to its head normal form or weak head normal form in case that the whn is not an implicit susp itself (in which case the term skeleton must be an abstraction). */ static DF_TermPtr HN_hnormSusp(DF_TermPtr suspPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned int myol, mynl ; // for book keeping the env of implicit susp DF_EnvPtr myenvlist; Boolean emptyTopEnv = HN_isEmptyEnv(); if (!emptyTopEnv){ myol = ol; mynl = nl; myenvlist = envlist; } //first (weak) head normalize the explicit susp HN_setEnv(DF_suspOL(suspPtr), DF_suspNL(suspPtr), DF_suspEnv(suspPtr)); rtPtr = HN_hnormDispatch(DF_suspTermSkel(suspPtr), whnf); if (emptyTopEnv) { if (HN_isEmptyEnv()) { HNL_updateToRef(suspPtr, rtPtr); } } else { // ! emptyTopEnv if (HN_isEmptyEnv()) HNL_updateToRef(suspPtr, rtPtr); else rtPtr = HN_pushSuspOverLam(rtPtr); //(weak) head norm the top-level (imp) susp HN_setEnv(myol, mynl, myenvlist); /* note that AM_numabs, AM_numargs and AM_argvec have to be re-initialized, because the (w)hnf of the inner suspension is to be traversed again. */ HNL_initRegs(); rtPtr = HN_hnormDispatch(rtPtr, whnf); } return rtPtr; } /****************************************************************************/ /* Dispatching on various term categories. */ /****************************************************************************/ static DF_TermPtr HN_hnormDispatch(DF_TermPtr tmPtr, Boolean whnf) { restart: switch (DF_termTag(tmPtr)){ case DF_TM_TAG_VAR: { if (!HN_isEmptyEnv()) HN_setEmptyEnv(); HNL_setRegsFlex(tmPtr); return tmPtr; } case DF_TM_TAG_CONST: case DF_TM_TAG_INT: case DF_TM_TAG_FLOAT: case DF_TM_TAG_NIL: case DF_TM_TAG_STR: case DF_TM_TAG_STREAM: { if (!HN_isEmptyEnv()) HN_setEmptyEnv(); HNL_setRegsRig(tmPtr); return tmPtr; } case DF_TM_TAG_BVAR: { return HN_hnormBV(tmPtr, whnf); } case DF_TM_TAG_CONS: { return HN_hnormCons(tmPtr, whnf); } case DF_TM_TAG_LAM: { return HN_hnormLam(tmPtr, whnf); } case DF_TM_TAG_APP: { return HN_hnormApp(tmPtr, whnf); } case DF_TM_TAG_SUSP: { return HN_hnormSusp(tmPtr, whnf); } case DF_TM_TAG_REF: { tmPtr = DF_termDeref(tmPtr); goto restart;} } //Impossible to reach this point. return NULL; } /****************************************************************************/ /* the interface routine for head normalization */ /****************************************************************************/ void HN_hnorm(DF_TermPtr tmPtr) { HN_setEmptyEnv(); HNL_initRegs(); HN_hnormDispatch(DF_termDeref(tmPtr), FALSE); } /****************************************************************************/ /* HEAD (WEAK HEAD) NORMALIZATION WITH OCCURS CHECK */ /*--------------------------------------------------------------------------*/ /* General comments: */ /* Checkings are added when the (dereference of) term to be normlized is */ /* an application or a cons. If the term is an application, checking is */ /* made on whether the application is currently referred */ /* to by register AM_vbbreg, and this checking is added in the APP case */ /* of the dispatch function. If the term is a cons, checking is made on */ /* whether its argument vector is currently referred to by the register */ /* AM_vbbreg, and this checking is added in sub-function HN_hnormConsOcc. */ /****************************************************************************/ static DF_TermPtr HN_hnormDispatchOcc(DF_TermPtr tmPtr, Boolean whnf); /****************************************************************************/ /* functions for (weak) head normalizing terms with occurs-check */ /* of known categories */ /****************************************************************************/ /* (weak) head normalize bound variable or implicit suspension with bound variable as term skeleton. */ static DF_TermPtr HN_hnormBVOcc(DF_TermPtr bvPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned if (HN_isEmptyEnv()){ //[|#i, 0, 0, nil|] -> #i rtPtr = bvPtr; HNL_setRegsRig(bvPtr); } else { //non-empty env int dbind = DF_bvIndex(bvPtr); if (dbind > ol) { //[|#i,ol,nl,e|] -> #i-ol+nl int newind = dbind - ol + nl; AM_embedError(newind); rtPtr =(DF_TermPtr)AM_hreg; HNL_pushBV(newind); HNL_setRegsRig(rtPtr); HN_setEmptyEnv(); } else { // i <= ol DF_EnvPtr envitem = DF_envListNth(envlist, dbind); int nladj = nl-DF_envIndex(envitem); if (DF_isDummyEnv(envitem)){ //[|#i,ol,nl,..@l..|]->#(nl-l) rtPtr = (DF_TermPtr)AM_hreg; HNL_pushBV(nladj); HNL_setRegsRig(rtPtr); HN_setEmptyEnv(); } else { //pair env [|#i,ol,nl,..(s,l)..|] -> [|s,0,(nl-l),nil|] DF_TermPtr tmPtr = DF_termDeref(DF_envPairTerm(envitem)); if ((nladj != 0) && (DF_isSusp(tmPtr))) {//combine susp int newnl = DF_suspNL(tmPtr)+nladj; AM_embedError(newnl); HN_setEnv(DF_suspOL(tmPtr), newnl, DF_suspEnv(tmPtr)); rtPtr = HN_hnormDispatchOcc(DF_suspTermSkel(tmPtr), whnf); } else { HN_setEnv(0, nladj, DF_EMPTY_ENV); rtPtr = HN_hnormDispatchOcc(tmPtr, whnf); } } //pair env } // i<= ol } //non-empty env return rtPtr; } /* (weak) head normalize an abstraction or implicit suspension with term skeleton as an abstraction. */ static DF_TermPtr HN_hnormLamOcc(DF_TermPtr lamPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned if (whnf) return rtPtr = lamPtr; //weak hn else { //whnf = FALSE int numabs = DF_lamNumAbs(lamPtr); DF_TermPtr newbody; if (HN_isEmptyEnv()){ newbody = HN_hnormDispatchOcc(DF_lamBody(lamPtr), FALSE); rtPtr = lamPtr; //body must have been adjusted in place } else { // non-empty env //[|lam(n,t),ol,nl,e|] ->lam(n,[|t,ol+n,nl+n,@nl+n-1...::@nl::e|] int newol = ol+numabs, newnl = nl+numabs; AM_embedError(newol); AM_embedError(newnl); HN_setEnv(newol, newnl, HN_addNDummyEnv(numabs)); newbody = HN_hnormDispatchOcc(DF_lamBody(lamPtr), FALSE); /* create a new lam on the result of hn the lam body */ rtPtr = (DF_TermPtr)AM_hreg; HNL_pushLam(newbody, numabs); } // non-empty env AM_numAbs += numabs; } //whnf == FALSE return rtPtr; } /* (weak) head normalize cons or implicit suspension over cons. Note checking on whether the argument vector of the cons term is referred to by the register AM_vbbreg is made. */ static DF_TermPtr HN_hnormConsOcc(DF_TermPtr consPtr, Boolean whnf) { DF_TermPtr argvec = DF_consArgs(consPtr), rtPtr; //term pointer to be returned if (AM_vbbreg == argvec) EM_THROW(EM_FAIL); if (HN_isEmptyEnv()){ AM_argVec = argvec; AM_numArgs = DF_CONS_ARITY; rtPtr = consPtr; } else { Boolean changed = HNL_makeConsArgvec(argvec, ol, nl, envlist); if (changed){ //new argvec is built because of pushing susp rtPtr = (DF_TermPtr)AM_hreg; HNL_pushCons(AM_argVec); } else rtPtr = consPtr; HN_setEmptyEnv(); } HNL_setRegsCons(rtPtr); return rtPtr; } /* (weak) head normalize application or implicit suspension over application. */ static DF_TermPtr HN_hnormAppOcc(DF_TermPtr appPtr, Boolean whnf) { DF_TermPtr funPtr = DF_appFunc(appPtr), argvec = DF_appArgs(appPtr), rtPtr; // term pointer to be returned DF_TermPtr oldFunPtr = funPtr; int arity = DF_appArity(appPtr); Boolean emptyTopEnv = HN_isEmptyEnv(); int myol, mynl; //for book keeping the implicit suspension env DF_EnvPtr myenvlist; //for book keeping the implicit suspension env int myarity = arity; //book keeping the arity before contraction if (!emptyTopEnv) { //book keeping the current environment myol = ol; mynl = nl; myenvlist = envlist; } funPtr = HN_hnormDispatchOcc(funPtr, TRUE); //whf of the function while ((arity > 0) && (DF_isLam(funPtr))) { //perform contraction on top-level redexes while you can DF_TermPtr lamBody = DF_lamBody(funPtr); //abs body int numAbsInFun = DF_lamNumAbs(funPtr); int numContract = ((arity<=numAbsInFun) ? arity : numAbsInFun); DF_EnvPtr newenv; int newol = ol + numContract; AM_embedError(newol); if (emptyTopEnv) newenv = HN_addNPairEmpEnv(argvec, numContract); else newenv = HN_addNPair(argvec, myol, mynl, myenvlist, numContract); HN_setEnv(newol, nl, newenv); if (arity == numAbsInFun){ funPtr = HN_hnormDispatchOcc(lamBody, whnf); arity = 0; } else if (arity > numAbsInFun) { funPtr = HN_hnormDispatchOcc(lamBody, TRUE); argvec=(DF_TermPtr)(((MemPtr)argvec)+numAbsInFun*DF_TM_ATOMIC_SIZE); arity -= numAbsInFun; } else { //arity < numabsInFun DF_TermPtr newBody = (DF_TermPtr)AM_hreg; HNL_pushLam(lamBody, (numAbsInFun-arity)); funPtr = HN_hnormDispatchOcc(newBody, whnf); arity = 0; } }// while ((arity >0) && (DF_IsLam(fun))) //update or create application if (arity == 0) { //app disappears rtPtr = funPtr; if (emptyTopEnv && HN_isEmptyEnv()) HNL_updateToRef(appPtr, funPtr); } else { //app persists; Note: now HN_isEmptyEnv must be TRUE Boolean changed; if (emptyTopEnv) changed = HNL_makeArgvecEmpEnv(argvec, arity); else changed = HNL_makeArgvec(argvec,arity,myol,mynl,myenvlist); if ((!changed) && (arity == myarity) && (oldFunPtr == funPtr)) { rtPtr = appPtr; } else {// create new app and in place update the old if empty top env rtPtr = (DF_TermPtr)AM_hreg; HNL_pushApp(AM_head, AM_argVec, AM_numArgs); if (emptyTopEnv) HNL_updateToRef(appPtr, rtPtr); } } return rtPtr; } /* (weak) head normalize (explicit) suspension or implicit suspension with a suspension term skeletion. */ static DF_TermPtr HN_hnormSuspOcc(DF_TermPtr suspPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned int myol, mynl ; // for book keeping the env of implicit susp DF_EnvPtr myenvlist; Boolean emptyTopEnv = HN_isEmptyEnv(); if (!emptyTopEnv){ myol = ol; mynl = nl; myenvlist = envlist; } //first (weak) head normalize the explicit susp HN_setEnv(DF_suspOL(suspPtr), DF_suspNL(suspPtr), DF_suspEnv(suspPtr)); rtPtr = HN_hnormDispatchOcc(DF_suspTermSkel(suspPtr), whnf); if (emptyTopEnv) { if (HN_isEmptyEnv()) HNL_updateToRef(suspPtr, rtPtr); } else { // ! emptyTopEnv if (HN_isEmptyEnv()) HNL_updateToRef(suspPtr, rtPtr); else rtPtr = HN_pushSuspOverLam(rtPtr); //(weak) head norm the top-level (imp) susp HN_setEnv(myol, mynl, myenvlist); /* note that AM_numabs, AM_numargs and AM_argvec have to be re-initialized, because the (w)hnf of the inner suspension is to be traversed again. */ HNL_initRegs(); rtPtr = HN_hnormDispatchOcc(rtPtr, whnf); } return rtPtr; } /****************************************************************************/ /* Dispatching on various term categories. */ /****************************************************************************/ static DF_TermPtr HN_hnormDispatchOcc(DF_TermPtr tmPtr, Boolean whnf) { restart_hnormOcc: switch (DF_termTag(tmPtr)){ case DF_TM_TAG_VAR: { if (!HN_isEmptyEnv()) HN_setEmptyEnv(); HNL_setRegsFlex(tmPtr); return tmPtr; } case DF_TM_TAG_CONST: case DF_TM_TAG_INT: case DF_TM_TAG_FLOAT: case DF_TM_TAG_NIL: case DF_TM_TAG_STR: case DF_TM_TAG_STREAM: { if (!HN_isEmptyEnv()) HN_setEmptyEnv(); HNL_setRegsRig(tmPtr); return tmPtr; } case DF_TM_TAG_BVAR: { return HN_hnormBVOcc(tmPtr, whnf); } case DF_TM_TAG_CONS: { return HN_hnormConsOcc(tmPtr, whnf); } case DF_TM_TAG_LAM: { return HN_hnormLamOcc(tmPtr, whnf); } case DF_TM_TAG_APP: { if (AM_vbbreg == tmPtr) EM_THROW(EM_FAIL); return HN_hnormAppOcc(tmPtr, whnf); } case DF_TM_TAG_SUSP: { return HN_hnormSuspOcc(tmPtr, whnf); } case DF_TM_TAG_REF: {tmPtr=DF_termDeref(tmPtr); goto restart_hnormOcc;} } //Impossible to reach this point. return NULL; } /****************************************************************************/ /* the interface routine for head normalization */ /****************************************************************************/ void HN_hnormOcc(DF_TermPtr tmPtr) { HN_setEmptyEnv(); HNL_initRegs(); tmPtr = HN_hnormDispatchOcc(DF_termDeref(tmPtr), FALSE); } /****************************************************************************/ /* FULL NORMALIZATION */ /****************************************************************************/ static DF_TermPtr HN_lnormDispatch(DF_TermPtr, Boolean whnf); /****************************************************************************/ /* Functions for creating argument vectors in full normalization */ /*--------------------------------------------------------------------------*/ /* General comments: */ /* This is the counter part of HNL_makeArgvec functions (hnormlocal.c) */ /* in the full normalization process for arranging arguments of */ /* applications (cons) when their "heads" are in (head) normal forms. */ /* Nested applications are unfolded. */ /* The difference is that HN_lnormDispatch is invoked on each argument */ /* to fully normalize it. */ /****************************************************************************/ /* Fully normalize (implicit) suspensions [| ai, myol, mynl, myenv |], where ai's are those in the vector referred to by argvec with size arity, and myol, mynl, myenv are given by other parameters. Note that a new argument vector is always created. */ static void HN_lnormArgvec(DF_TermPtr argvec, int arity, int myol, int mynl, DF_EnvPtr myenv) { int i; //book keeping relevant regs. DF_TermPtr head = AM_head, myArgvec = AM_argVec; int numAbs = AM_numAbs, numArgs = AM_numArgs; Flag rigFlag = AM_rigFlag, consFlag = AM_consFlag; MemPtr newArgvec = AM_hreg; //new argvec MemPtr newhtop = newArgvec + arity * DF_TM_ATOMIC_SIZE; AM_heapError(newhtop); AM_hreg = newhtop; //arrange heap top for creating terms in norm args for (i = 1; i <= arity; i++){ HN_setEnv(myol, mynl, myenv); //imp susp environment HNL_initRegs(); DF_mkRef(newArgvec, HN_lnormDispatch(argvec, FALSE)); newArgvec += DF_TM_ATOMIC_SIZE; argvec = (DF_TermPtr)(((MemPtr)argvec)+DF_TM_ATOMIC_SIZE); } //reset registers AM_head = head; AM_argVec = myArgvec; AM_numAbs = numAbs; AM_numArgs = numArgs; AM_rigFlag = rigFlag; AM_consFlag = consFlag; } /* A specialized version of HN_lnormArgvec when the implicit suspension over each argument in the vector is known to be empty. Note that upon the return of HN_lnormDispatch, the argument has been destructively updated to its normal form, which means the old argument vector is always used. */ static void HN_lnormArgvecEmpEnv(DF_TermPtr argvec, int arity) { int i; //book keeping relevant regs. DF_TermPtr head = AM_head, myArgvec = AM_argVec; int numAbs = AM_numAbs, numArgs = AM_numArgs; Flag rigFlag = AM_rigFlag, consFlag = AM_consFlag; for (i = 1; i <= arity; i++){ HNL_initRegs(); HN_lnormDispatch(argvec, FALSE); argvec = (DF_TermPtr)(((MemPtr)argvec) + DF_TM_ATOMIC_SIZE); } //reset registers AM_head = head; AM_argVec = myArgvec; AM_numAbs = numAbs; AM_numArgs = numArgs; AM_rigFlag = rigFlag; AM_consFlag = consFlag; } /* Create an argument vector for applications within a non-empty environment. Actions are carried out in two steps: First, nested applications are unfolded if arising. Second, the (implicit) suspensions formed by each argument and given parameters are fully normalized. Note that a new argument vector is always created. */ static Boolean HN_makeArgvecLnorm(DF_TermPtr argvec, int arity, int myol, int mynl, DF_EnvPtr myenv) { DF_TermPtr newArgvec = (DF_TermPtr)AM_hreg; //new argvec int newArity; if (AM_numArgs != 0){ //unfold nested app first MemPtr newhtop = AM_hreg + AM_numArgs * DF_TM_ATOMIC_SIZE; AM_heapError(newhtop); newArity = arity + AM_numArgs; AM_arityError(newArity); HNL_copyArgs(AM_argVec, AM_numArgs); //layout inner args } else newArity = arity; //fully normalize arguments HN_lnormArgvec(argvec, arity, myol, mynl, myenv); AM_argVec = newArgvec; AM_numArgs = newArity; return TRUE; } /* A specilized version of HN_makeArgvecLnorm when the enclosing environment is known to be empty. Note that new argument vecoter is created if nested applications were unfolded. Otherwise, the old is used. Boolean values TRUE or FALSE is returned to inidicate which situation it is. */ static Boolean HN_makeArgvecEmpEnvLnorm(DF_TermPtr argvec, int arity) { HN_lnormArgvecEmpEnv(argvec, arity); //lnorm arguments if (AM_numArgs != 0){ //unfold nested app int newArity = arity + AM_numArgs; DF_TermPtr newArgvec = (DF_TermPtr)AM_hreg; //new argument vector AM_arityError(newArity); AM_heapError(((MemPtr)newArgvec + newArity * DF_TM_ATOMIC_SIZE)); HNL_copyArgs(AM_argVec, AM_numArgs); HNL_copyArgs(argvec, arity); AM_argVec = newArgvec; AM_numArgs = newArity; return TRUE; } else { AM_argVec = argvec; AM_numArgs = arity; return FALSE; } } /****************************************************************************/ /* functions for fully normalizing terms of known categories */ /*--------------------------------------------------------------------------*/ /* General comments: */ /* */ /* An implicit suspension is given by the global variables ol, nl and */ /* envlist together with the first argument tmPtr to the sub-functions: */ /* [|tmPtr, ol, nl, envlist|] */ /* The suspension environment could be empty in which case the term */ /* being normalized is tmPtr itself. */ /* The second argument of the sub-functions whnf is a flag indicating */ /* whether a head normal form or a weak head normal form is being */ /* computed. */ /****************************************************************************/ /* Fully normalize or weak head normalize bound variable or implicit suspension with bound variable as term skeleton. The actions carried out are the same as the counter part in the head normalization proceee, except that HN_lnormDispatch is invoked as opposed to HN_hnormDispatch when necessary. */ static DF_TermPtr HN_lnormBV(DF_TermPtr bvPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned if (HN_isEmptyEnv()){ //[|#i, 0, 0, nil|] -> #i rtPtr = bvPtr; HNL_setRegsRig(bvPtr); } else { //non-empty env int dbind = DF_bvIndex(bvPtr); if (dbind > ol) { //[|#i,ol,nl,e|] -> #i-ol+nl int newind = dbind - ol + nl; AM_embedError(newind); rtPtr =(DF_TermPtr)AM_hreg; HNL_pushBV(newind); HNL_setRegsRig(rtPtr); HN_setEmptyEnv(); } else { // i <= ol DF_EnvPtr envitem = DF_envListNth(envlist, dbind); int nladj = nl-DF_envIndex(envitem); if (DF_isDummyEnv(envitem)){ //[|#i,ol,nl,..@l..|]->#(nl-l) rtPtr = (DF_TermPtr)AM_hreg; HNL_pushBV(nladj); HNL_setRegsRig(rtPtr); HN_setEmptyEnv(); } else { //pair env [|#i,ol,nl,..(s,l)..|] -> [|s,0,(nl-l),nil|] DF_TermPtr tmPtr = DF_termDeref(DF_envPairTerm(envitem)); if ((nladj != 0) && (DF_isSusp(tmPtr))) {//combine susp int newnl = DF_suspNL(tmPtr)+nladj; AM_embedError(newnl); HN_setEnv(DF_suspOL(tmPtr), newnl, DF_suspEnv(tmPtr)); rtPtr = HN_lnormDispatch(DF_suspTermSkel(tmPtr), whnf); } else { HN_setEnv(0, nladj, DF_EMPTY_ENV); rtPtr = HN_lnormDispatch(tmPtr, whnf); } } //pair env } // i<= ol } //non-empty env return rtPtr; } /* Fully normalize or weak head normalize abstractions or implicit suspension with abstractions as term skeletons. The actions carried out are the same as the counter part in the head normalization process, except that HN_lnormDispatch is invoked as opposed to HN_hnormDispatch when necessary. */ static DF_TermPtr HN_lnormLam(DF_TermPtr lamPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned if (whnf) return rtPtr = lamPtr; //weak hn else { //whnf = FALSE int numabs = DF_lamNumAbs(lamPtr); DF_TermPtr newbody; if (HN_isEmptyEnv()){ newbody = HN_lnormDispatch(DF_lamBody(lamPtr), FALSE); rtPtr = lamPtr; //body must have been adjusted in place } else { // non-empty env //[|lam(n,t),ol,nl,e|] ->lam(n,[|t,ol+n,nl+n,@nl+n-1...::@nl::e|] int newol = ol+numabs, newnl = nl+numabs; AM_embedError(newol); AM_embedError(newnl); HN_setEnv(newol, newnl, HN_addNDummyEnv(numabs)); newbody = HN_lnormDispatch(DF_lamBody(lamPtr), FALSE); /* create a new lam on the result of hn the lam body */ rtPtr = (DF_TermPtr)AM_hreg; HNL_pushLam(newbody, numabs); } // non-empty env AM_numAbs += numabs; } //whnf == FALSE return rtPtr; } /* Fully normalize or weak head normalize cons or implicit suspension over cons. The difference from HN_hnormCons is that the arguments of the cons are fully normalized. */ static DF_TermPtr HN_lnormCons(DF_TermPtr consPtr, Boolean whnf) { DF_TermPtr argvec = DF_consArgs(consPtr), rtPtr; //term pointer to be returned if (HN_isEmptyEnv()){ HN_lnormArgvecEmpEnv(argvec, DF_CONS_ARITY); AM_argVec = argvec; AM_numArgs = DF_CONS_ARITY; rtPtr = consPtr; } else { DF_TermPtr newArgvec = (DF_TermPtr)AM_hreg; //new argument vector HN_lnormArgvec(argvec, DF_CONS_ARITY, ol, nl, envlist); AM_argVec = newArgvec; AM_numArgs = DF_CONS_ARITY; rtPtr = (DF_TermPtr)AM_hreg; HNL_pushCons(AM_argVec); HN_setEmptyEnv(); } HNL_setRegsCons(rtPtr); return rtPtr; } /* Fully normalize or weak head normalize application or implicit suspension over application. The actions carried out here is the same as those in HN_hnormApp except that HN_lnormDispatch is invoked as HN_hnormDispatch, and in making argument vectors makeArgvecLnorm functions are used to fully normalize the arguments. */ static DF_TermPtr HN_lnormApp(DF_TermPtr appPtr, Boolean whnf) { DF_TermPtr funPtr = DF_appFunc(appPtr), argvec = DF_appArgs(appPtr), rtPtr; // term pointer to be returned DF_TermPtr oldFunPtr = funPtr; int arity = DF_appArity(appPtr); Boolean emptyTopEnv = HN_isEmptyEnv(); int myol, mynl; //for book keeping the implicit suspension env DF_EnvPtr myenvlist; //for book keeping the implicit suspension env int myarity = arity; //book keeping the arity before contraction if (!emptyTopEnv) { //book keeping the current environment myol = ol; mynl = nl; myenvlist = envlist; } funPtr = HN_lnormDispatch(funPtr, TRUE); //whf of the function while ((arity > 0) && (DF_isLam(funPtr))) { //perform contraction on top-level redexes while you can DF_TermPtr lamBody = DF_lamBody(funPtr); //abs body int numAbsInFun = DF_lamNumAbs(funPtr); int numContract = ((arity<=numAbsInFun) ? arity : numAbsInFun); DF_EnvPtr newenv; int newol = ol + numContract; AM_embedError(newol); if (emptyTopEnv) newenv = HN_addNPairEmpEnv(argvec, numContract); else newenv = HN_addNPair(argvec, myol, mynl, myenvlist, numContract); HN_setEnv(newol, nl, newenv); if (arity == numAbsInFun){ funPtr = HN_lnormDispatch(lamBody, whnf); arity = 0; } else if (arity > numAbsInFun) { funPtr = HN_lnormDispatch(lamBody, TRUE); argvec=(DF_TermPtr)(((MemPtr)argvec)+numAbsInFun*DF_TM_ATOMIC_SIZE); arity -= numAbsInFun; } else { //arity < numabsInFun DF_TermPtr newBody = (DF_TermPtr)AM_hreg; HNL_pushLam(lamBody, (numAbsInFun-arity)); funPtr = HN_lnormDispatch(newBody, whnf); arity = 0; } }// while ((arity >0) && (DF_IsLam(fun))) //update or create application if (arity == 0) { //app disappears rtPtr = funPtr; if (emptyTopEnv && HN_isEmptyEnv()) HNL_updateToRef(appPtr, funPtr); } else { //app persists; Note: now HN_isEmptyEnv must be TRUE Boolean changed; if (emptyTopEnv) changed = HN_makeArgvecEmpEnvLnorm(argvec, arity); else changed = HN_makeArgvecLnorm(argvec,arity,myol,mynl,myenvlist); if ((!changed) && (arity == myarity) && (oldFunPtr == funPtr)) { rtPtr = appPtr; } else {// create new app and in place update the old if empty top env rtPtr = (DF_TermPtr)AM_hreg; HNL_pushApp(AM_head, AM_argVec, AM_numArgs); if (emptyTopEnv) HNL_updateToRef(appPtr, rtPtr); } } return rtPtr; } /* Fuuly normlize or weak head normalize (explicit) suspension or implicit suspension with a suspension term skeletion. The actions are the same as those in HN_hnormSusp except that HN_lnormDispatch is used as opposed to HN_hnormSusp with one exception: when the environment of the top-level suspension is not empty, the inner suspension is head normalized (HN_hnormDispatch). */ static DF_TermPtr HN_lnormSusp(DF_TermPtr suspPtr, Boolean whnf) { DF_TermPtr rtPtr; //term pointer to be returned int myol, mynl; // for book keeping the env of implicit susp DF_EnvPtr myenvlist; Boolean emptyTopEnv = HN_isEmptyEnv(); if (!emptyTopEnv) { myol = ol; mynl = nl; myenvlist = envlist; } HN_setEnv(DF_suspOL(suspPtr), DF_suspNL(suspPtr), DF_suspEnv(suspPtr)); if (emptyTopEnv){ rtPtr = HN_lnormDispatch(DF_suspTermSkel(suspPtr), whnf); if (HN_isEmptyEnv()) HNL_updateToRef(suspPtr, rtPtr); } else { //non-empty top-level env rtPtr = HN_hnormDispatch(DF_suspTermSkel(suspPtr), whnf); if (HN_isEmptyEnv()) HNL_updateToRef(suspPtr, rtPtr); else rtPtr = HN_pushSuspOverLam(rtPtr); //fully normalize top-level susp HN_setEnv(myol, mynl, myenvlist); /* note that AM_numabs, AM_numargs and AM_argvec have to be re-initialized, because the (w)hnf of the inner suspension is to be traversed again. */ HNL_initRegs(); rtPtr = HN_lnormDispatch(rtPtr, whnf); } return rtPtr; } /****************************************************************************/ /* Dispatching on various term categories. */ /****************************************************************************/ static DF_TermPtr HN_lnormDispatch(DF_TermPtr tmPtr, Boolean whnf) { restart_lnorm: switch (DF_termTag(tmPtr)){ case DF_TM_TAG_VAR: { if (!HN_isEmptyEnv()) HN_setEmptyEnv(); HNL_setRegsFlex(tmPtr); return tmPtr; } case DF_TM_TAG_CONST: case DF_TM_TAG_INT: case DF_TM_TAG_FLOAT: case DF_TM_TAG_NIL: case DF_TM_TAG_STR: case DF_TM_TAG_STREAM: { if (!HN_isEmptyEnv()) HN_setEmptyEnv(); HNL_setRegsRig(tmPtr); return tmPtr; } case DF_TM_TAG_BVAR: { return HN_lnormBV(tmPtr, whnf); } case DF_TM_TAG_CONS: { return HN_lnormCons(tmPtr, whnf); } case DF_TM_TAG_LAM: { return HN_lnormLam(tmPtr, whnf); } case DF_TM_TAG_APP: { return HN_lnormApp(tmPtr, whnf); } case DF_TM_TAG_SUSP: { return HN_lnormSusp(tmPtr, whnf); } case DF_TM_TAG_REF: { tmPtr = DF_termDeref(tmPtr); goto restart_lnorm;} } //Impossible to reach this point. return NULL; } /****************************************************************************/ /* the interface routine for head normalization */ /****************************************************************************/ void HN_lnorm(DF_TermPtr tmPtr) { HN_setEmptyEnv(); HNL_initRegs(); tmPtr = HN_lnormDispatch(DF_termDeref(tmPtr), FALSE); } #endif //HNORM_C