/tcllib-1.11.1/modules/struct/tree/walk.c
# · C · 708 lines · 483 code · 145 blank · 80 comment · 161 complexity · d4be23d5bb3c6588260f3e9c9a333b20 MD5 · raw file
- #include "tcl.h"
- #include <t.h>
- #include <util.h>
- /* .................................................. */
- static int t_walkdfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action);
- static int t_walkdfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action);
- static int t_walkdfsin (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action);
- static int t_walkdfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* enter, Tcl_Obj* leave);
- static int t_walkbfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action);
- static int t_walkbfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action);
- static int t_walkbfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* enter, Tcl_Obj* leave);
- /* .................................................. */
- int
- t_walkoptions (Tcl_Interp* interp, int n,
- int objc, Tcl_Obj* CONST* objv,
- int* type, int* order, int* remainder,
- char* usage)
- {
- int i;
- Tcl_Obj* otype = NULL;
- Tcl_Obj* oorder = NULL;
- static CONST char* wtypes [] = {
- "bfs", "dfs", NULL
- };
- static CONST char* worders [] = {
- "both", "in", "pre", "post", NULL
- };
- for (i = 3; i < objc; ) {
- ASSERT_BOUNDS (i, objc);
- if (0 == strcmp ("-type", Tcl_GetString (objv [i]))) {
- if (objc == (i+1)) {
- Tcl_AppendResult (interp,
- "value for \"-type\" missing",
- NULL);
- return TCL_ERROR;
- }
- ASSERT_BOUNDS (i+1, objc);
- otype = objv [i+1];
- i += 2;
- } else if (0 == strcmp ("-order", Tcl_GetString (objv [i]))) {
- if (objc == (i+1)) {
- Tcl_AppendResult (interp,
- "value for \"-order\" missing",
- NULL);
- return TCL_ERROR;
- }
- ASSERT_BOUNDS (i+1, objc);
- oorder = objv [i+1];
- i += 2;
- } else if (0 == strcmp ("--", Tcl_GetString (objv [i]))) {
- i++;
- break;
- } else {
- break;
- }
- }
- if (i == objc) {
- Tcl_WrongNumArgs (interp, 2, objv, usage);
- return TCL_ERROR;
- }
- if ((objc - i) > n) {
- Tcl_AppendResult (interp, "unknown option \"", NULL);
- Tcl_AppendResult (interp, Tcl_GetString (objv [i]), NULL);
- Tcl_AppendResult (interp, "\"", NULL);
- return TCL_ERROR;
- }
- if (!otype) {
- *type = WT_DFS;
- } else if (Tcl_GetIndexFromObj (interp, otype, wtypes, "search type",
- 0, type) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!oorder) {
- *order = WO_PRE;
- } else if (Tcl_GetIndexFromObj (interp, oorder, worders, "search order",
- 0, order) != TCL_OK) {
- return TCL_ERROR;
- }
- if ((*order == WO_IN) && (*type == WT_BFS)) {
- Tcl_AppendResult (interp,
- "unable to do a in-order breadth first walk",
- NULL);
- return TCL_ERROR;
- }
- *remainder = i;
- return TCL_OK;
- }
- /* .................................................. */
- int
- t_walk (Tcl_Interp* interp, TN* tdn, int type, int order,
- t_walk_function f, Tcl_Obj* cs,
- Tcl_Obj* avn, Tcl_Obj* nvn)
- {
- int res;
- Tcl_Obj* la = NULL;
- Tcl_Obj* lb = NULL;
- switch (type)
- {
- case WT_DFS:
- switch (order)
- {
- case WO_BOTH:
- la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
- lb = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (lb);
- res = t_walkdfsboth (interp, tdn, f, cs, avn, nvn, la, lb);
- Tcl_DecrRefCount (la);
- Tcl_DecrRefCount (lb);
- break;
- case WO_IN:
- la = Tcl_NewStringObj ("visit",-1); Tcl_IncrRefCount (la);
- res = t_walkdfsin (interp, tdn, f, cs, avn, nvn, la);
- Tcl_DecrRefCount (la);
- break;
- case WO_PRE:
- la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
- res = t_walkdfspre (interp, tdn, f, cs, avn, nvn, la);
- Tcl_DecrRefCount (la);
- break;
- case WO_POST:
- la = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (la);
- res = t_walkdfspost (interp, tdn, f, cs, avn, nvn, la);
- Tcl_DecrRefCount (la);
- break;
- }
- break;
- case WT_BFS:
- switch (order)
- {
- case WO_BOTH:
- la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
- lb = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (lb);
- res = t_walkbfsboth (interp, tdn, f, cs, avn, nvn, la, lb);
- Tcl_DecrRefCount (la);
- Tcl_DecrRefCount (lb);
- break;
- case WO_PRE:
- la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
- res = t_walkbfspre (interp, tdn, f, cs, avn, nvn, la);
- Tcl_DecrRefCount (la);
- break;
- case WO_POST:
- la = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (la);
- res = t_walkbfspost (interp, tdn, f, cs, avn, nvn, la);
- Tcl_DecrRefCount (la);
- break;
- }
- break;
- }
- /* Error and Return are passed unchanged. Everything else is ok */
- if (res == TCL_ERROR) {return res;}
- if (res == TCL_RETURN) {return res;}
- return TCL_OK;
- }
- /* .................................................. */
- int
- t_walk_invokescript (Tcl_Interp* interp, TN* n, Tcl_Obj* cs,
- Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action)
- {
- int res;
- /* Note: Array elements, like 'a(x)', are not possible as iterator variables */
- if (avn) {
- Tcl_ObjSetVar2 (interp, avn, NULL, action, 0);
- }
- Tcl_ObjSetVar2 (interp, nvn, NULL, n->name, 0);
- res = Tcl_EvalObj(interp, cs);
- return res;
- }
- int
- t_walk_invokecmd (Tcl_Interp* interp, TN* n, Tcl_Obj* dummy0,
- Tcl_Obj* dummy1, Tcl_Obj* dummy2,
- Tcl_Obj* action)
- {
- int res;
- int cc = (int) dummy0;
- Tcl_Obj** ev = (Tcl_Obj**) dummy1; /* cc+3 elements */
- ev [cc] = dummy2; /* Tree */
- ev [cc+1] = n->name; /* Node */
- ev [cc+2] = action; /* Action */
- Tcl_IncrRefCount (ev [cc]);
- Tcl_IncrRefCount (ev [cc+1]);
- Tcl_IncrRefCount (ev [cc+2]);
- res = Tcl_EvalObjv (interp, cc+3, ev, 0);
- Tcl_DecrRefCount (ev [cc]);
- Tcl_DecrRefCount (ev [cc+1]);
- Tcl_DecrRefCount (ev [cc+2]);
- return res;
- }
- /* .................................................. */
- static int
- t_walkdfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action)
- {
- /* ok - next node
- * error - abort walking
- * break - abort walking
- * continue - next node
- * return - abort walking
- * prune /5 - skip children, otherwise ok.
- */
- int res;
- /* Parent before children, action is 'enter'. */
- res = (*f) (interp, tdn, cs, avn, nvn, action);
- if (res == 5) {
- return TCL_OK;
- } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
- return res;
- }
- if (tdn->nchildren) {
- /* We make a copy of the child array. This emulates the behaviour of
- * the Tcl implementation, which will walk to a child of this node,
- * even if the loop body/procedure moved it to a different node before
- * it was reached by the loop here. If the node it the child is moved
- * to was already visited nothing else will happen. Ortherwise the
- * child will be visited multiple times.
- */
- int i;
- int nc = tdn->nchildren;
- TN** nv = NALLOC (nc,TN*);
- memcpy (nv, tdn->child, nc*sizeof(TN*));
- for (i = 0; i < nc; i++) {
- res = t_walkdfspre (interp, nv [i], f, cs, avn, nvn, action);
- /* prune, continue cannot occur, were transformed into ok
- * by the child.
- */
- if (res != TCL_OK) {
- ckfree ((char*) nv);
- return res;
- }
- }
- ckfree ((char*) nv);
- }
- return TCL_OK;
- }
- static int
- t_walkdfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action)
- {
- int res;
- /* Parent after children, action is 'leave'. */
- if (tdn->nchildren) {
- /* We make a copy of the child array. This emulates the behaviour of
- * the Tcl implementation, which will walk to a child of this node,
- * even if the loop body/procedure moved it to a different node before
- * it was reached by the loop here. If the node it the child is moved
- * to was already visited nothing else will happen. Ortherwise the
- * child will be visited multiple times.
- */
- int i;
- int nc = tdn->nchildren;
- TN** nv = NALLOC (nc,TN*);
- memcpy (nv, tdn->child, nc*sizeof(TN*));
- for (i = 0; i < nc; i++) {
- res = t_walkdfspost (interp, nv [i], f, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- ckfree ((char*) nv);
- return res;
- }
- }
- ckfree ((char*) nv);
- }
- res = (*f) (interp, tdn, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- return res;
- } else if (res == 5) {
- /* Illegal pruning */
- Tcl_ResetResult (interp);
- Tcl_AppendResult (interp,
- "Illegal attempt to prune post-order walking", NULL);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
- static int
- t_walkdfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* enter, Tcl_Obj* leave)
- {
- /* ok - next node
- * error - abort walking
- * break - abort walking
- * continue - next node
- * return - abort walking
- * prune /5 - skip children, otherwise ok.
- */
- int res;
- /* Parent before and after Children, action is 'enter' & 'leave'. */
- res = (*f) (interp, tdn, cs, avn, nvn, enter);
- if (res != 5) {
- if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
- return res;
- }
- if (tdn->nchildren) {
- int i;
- int nc = tdn->nchildren;
- TN** nv = NALLOC (nc,TN*);
- memcpy (nv, tdn->child, nc*sizeof(TN*));
- for (i = 0; i < nc; i++) {
- res = t_walkdfsboth (interp, nv [i], f, cs, avn, nvn, enter, leave);
- /* prune, continue cannot occur, were transformed into ok
- * by the child.
- */
- if (res != TCL_OK) {
- ckfree ((char*) nv);
- return res;
- }
- }
- ckfree ((char*) nv);
- }
- }
- res = (*f) (interp, tdn, cs, avn, nvn, leave);
- if (res == 5) {
- return TCL_OK;
- } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
- return res;
- }
- return TCL_OK;
- }
- static int
- t_walkdfsin (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action)
- {
- int res;
- /* First child visited first, then parent, then */
- /* the remaining children. Action is 'visit'. */
- /* This is the correct thing for binary trees. */
- /* For #children <= 1 the parent is visited */
- /* before the child */
- if (tdn->nchildren == 0) {
- res = (*f) (interp, tdn, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- return res;
- } else if (res == 5) {
- /* Illegal pruning */
- Tcl_ResetResult (interp);
- Tcl_AppendResult (interp,
- "Illegal attempt to prune in-order walking", NULL);
- return TCL_ERROR;
- }
- } else if (tdn->nchildren == 1) {
- res = (*f) (interp, tdn, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- return res;
- } else if (res == 5) {
- /* Illegal pruning */
- Tcl_ResetResult (interp);
- Tcl_AppendResult (interp,
- "Illegal attempt to prune in-order walking", NULL);
- return TCL_ERROR;
- }
- return t_walkdfsin (interp, tdn->child [0], f, cs, avn, nvn, action);
- } else {
- int i;
- int nc = tdn->nchildren;
- TN** nv = NALLOC (nc,TN*);
- memcpy (nv, tdn->child, nc*sizeof(TN*));
- res = t_walkdfsin (interp, tdn->child [0], f, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- ckfree ((char*) nv);
- return res;
- }
- res = (*f) (interp, tdn, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- ckfree ((char*) nv);
- return res;
- } else if (res == 5) {
- /* Illegal pruning */
- ckfree ((char*) nv);
- Tcl_ResetResult (interp);
- Tcl_AppendResult (interp,
- "Illegal attempt to prune in-order walking", NULL);
- return TCL_ERROR;
- }
- for (i = 1; i < nc; i++) {
- res = t_walkdfsin (interp, nv [i], f, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- ckfree ((char*) nv);
- return res;
- }
- }
- ckfree ((char*) nv);
- }
- return TCL_OK;
- }
- static int
- t_walkbfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* enter, Tcl_Obj* leave)
- {
- /* ok - next node
- * error - abort walking
- * break - pre: abort walking, skip to post, post: abort walking
- * continue - next node
- * return - abort walking
- * prune /5 - skip children, otherwise ok.
- */
- int res;
- TN* n;
- NLQ q;
- NLQ qb;
- nlq_init (&q);
- nlq_init (&qb);
- nlq_append (&q, tdn);
- nlq_push (&qb, tdn);
- while (1) {
- n = nlq_pop (&q);
- if (!n) break;
- res = (*f) (interp, n, cs, avn, nvn, enter);
- if (res == 5) {
- continue;
- } else if (res == TCL_ERROR) {
- nlq_clear (&q);
- nlq_clear (&qb);
- return res;
- } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
- nlq_clear (&q);
- /* We abort the collection of more nodes, but still run the
- * backward iteration (post-order phase).
- */
- break;
- }
- if (n->nchildren) {
- int i;
- for (i = 0; i < n->nchildren; i++) {
- nlq_append (&q, n->child [i]);
- nlq_push (&qb, n->child [i]);
- }
- }
- }
- /* Backward visit to leave */
- while (1) {
- n = nlq_pop (&qb);
- if (!n) break;
- res = (*f) (interp, n, cs, avn, nvn, leave);
- if (res == 5) {
- continue;
- } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
- nlq_clear (&qb);
- return res;
- }
- }
- return TCL_OK;
- }
- static int
- t_walkbfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action)
- {
- /* ok - next node
- * error - abort walking
- * break - abort walking
- * continue - next node
- * return - abort walking
- * prune /5 - skip children, otherwise ok.
- */
- int res;
- TN* n;
- NLQ q;
- nlq_init (&q);
- nlq_append (&q, tdn);
- while (1) {
- n = nlq_pop (&q);
- if (!n) break;
- res = (*f) (interp, n, cs, avn, nvn, action);
- if (res == 5) {
- continue;
- } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
- nlq_clear (&q);
- return res;
- }
- if (n->nchildren) {
- int i;
- for (i = 0; i < n->nchildren; i++) {
- nlq_append (&q, n->child [i]);
- }
- }
- }
- return TCL_OK;
- }
- static int
- t_walkbfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
- Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
- Tcl_Obj* action)
- {
- int res;
- TN* n;
- NLQ q;
- NLQ qb;
- nlq_init (&q);
- nlq_init (&qb);
- nlq_append (&q, tdn);
- nlq_push (&qb, tdn);
- while (1) {
- n = nlq_pop (&q);
- if (!n) break;
- if (n->nchildren) {
- int i;
- for (i = 0; i < n->nchildren; i++) {
- nlq_append (&q, n->child [i]);
- nlq_push (&qb, n->child [i]);
- }
- }
- }
- /* Backward visit to leave */
- while (1) {
- n = nlq_pop (&qb);
- if (!n) break;
- res = (*f) (interp, n, cs, avn, nvn, action);
- if ((res == TCL_ERROR) ||
- (res == TCL_BREAK) ||
- (res == TCL_RETURN)) {
- nlq_clear (&qb);
- return res;
- } else if (res == 5) {
- /* Illegal pruning */
- nlq_clear (&qb);
- Tcl_ResetResult (interp);
- Tcl_AppendResult (interp,
- "Illegal attempt to prune post-order walking", NULL);
- return TCL_ERROR;
- }
- }
- return TCL_OK;
- }
- /*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */