/tcllib-1.11.1/modules/struct/tree/walk.c

# · C · 708 lines · 483 code · 145 blank · 80 comment · 161 complexity · d4be23d5bb3c6588260f3e9c9a333b20 MD5 · raw file

  1. #include "tcl.h"
  2. #include <t.h>
  3. #include <util.h>
  4. /* .................................................. */
  5. static int t_walkdfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  6. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  7. Tcl_Obj* action);
  8. static int t_walkdfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  9. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  10. Tcl_Obj* action);
  11. static int t_walkdfsin (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  12. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  13. Tcl_Obj* action);
  14. static int t_walkdfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  15. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  16. Tcl_Obj* enter, Tcl_Obj* leave);
  17. static int t_walkbfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  18. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  19. Tcl_Obj* action);
  20. static int t_walkbfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  21. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  22. Tcl_Obj* action);
  23. static int t_walkbfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  24. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  25. Tcl_Obj* enter, Tcl_Obj* leave);
  26. /* .................................................. */
  27. int
  28. t_walkoptions (Tcl_Interp* interp, int n,
  29. int objc, Tcl_Obj* CONST* objv,
  30. int* type, int* order, int* remainder,
  31. char* usage)
  32. {
  33. int i;
  34. Tcl_Obj* otype = NULL;
  35. Tcl_Obj* oorder = NULL;
  36. static CONST char* wtypes [] = {
  37. "bfs", "dfs", NULL
  38. };
  39. static CONST char* worders [] = {
  40. "both", "in", "pre", "post", NULL
  41. };
  42. for (i = 3; i < objc; ) {
  43. ASSERT_BOUNDS (i, objc);
  44. if (0 == strcmp ("-type", Tcl_GetString (objv [i]))) {
  45. if (objc == (i+1)) {
  46. Tcl_AppendResult (interp,
  47. "value for \"-type\" missing",
  48. NULL);
  49. return TCL_ERROR;
  50. }
  51. ASSERT_BOUNDS (i+1, objc);
  52. otype = objv [i+1];
  53. i += 2;
  54. } else if (0 == strcmp ("-order", Tcl_GetString (objv [i]))) {
  55. if (objc == (i+1)) {
  56. Tcl_AppendResult (interp,
  57. "value for \"-order\" missing",
  58. NULL);
  59. return TCL_ERROR;
  60. }
  61. ASSERT_BOUNDS (i+1, objc);
  62. oorder = objv [i+1];
  63. i += 2;
  64. } else if (0 == strcmp ("--", Tcl_GetString (objv [i]))) {
  65. i++;
  66. break;
  67. } else {
  68. break;
  69. }
  70. }
  71. if (i == objc) {
  72. Tcl_WrongNumArgs (interp, 2, objv, usage);
  73. return TCL_ERROR;
  74. }
  75. if ((objc - i) > n) {
  76. Tcl_AppendResult (interp, "unknown option \"", NULL);
  77. Tcl_AppendResult (interp, Tcl_GetString (objv [i]), NULL);
  78. Tcl_AppendResult (interp, "\"", NULL);
  79. return TCL_ERROR;
  80. }
  81. if (!otype) {
  82. *type = WT_DFS;
  83. } else if (Tcl_GetIndexFromObj (interp, otype, wtypes, "search type",
  84. 0, type) != TCL_OK) {
  85. return TCL_ERROR;
  86. }
  87. if (!oorder) {
  88. *order = WO_PRE;
  89. } else if (Tcl_GetIndexFromObj (interp, oorder, worders, "search order",
  90. 0, order) != TCL_OK) {
  91. return TCL_ERROR;
  92. }
  93. if ((*order == WO_IN) && (*type == WT_BFS)) {
  94. Tcl_AppendResult (interp,
  95. "unable to do a in-order breadth first walk",
  96. NULL);
  97. return TCL_ERROR;
  98. }
  99. *remainder = i;
  100. return TCL_OK;
  101. }
  102. /* .................................................. */
  103. int
  104. t_walk (Tcl_Interp* interp, TN* tdn, int type, int order,
  105. t_walk_function f, Tcl_Obj* cs,
  106. Tcl_Obj* avn, Tcl_Obj* nvn)
  107. {
  108. int res;
  109. Tcl_Obj* la = NULL;
  110. Tcl_Obj* lb = NULL;
  111. switch (type)
  112. {
  113. case WT_DFS:
  114. switch (order)
  115. {
  116. case WO_BOTH:
  117. la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
  118. lb = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (lb);
  119. res = t_walkdfsboth (interp, tdn, f, cs, avn, nvn, la, lb);
  120. Tcl_DecrRefCount (la);
  121. Tcl_DecrRefCount (lb);
  122. break;
  123. case WO_IN:
  124. la = Tcl_NewStringObj ("visit",-1); Tcl_IncrRefCount (la);
  125. res = t_walkdfsin (interp, tdn, f, cs, avn, nvn, la);
  126. Tcl_DecrRefCount (la);
  127. break;
  128. case WO_PRE:
  129. la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
  130. res = t_walkdfspre (interp, tdn, f, cs, avn, nvn, la);
  131. Tcl_DecrRefCount (la);
  132. break;
  133. case WO_POST:
  134. la = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (la);
  135. res = t_walkdfspost (interp, tdn, f, cs, avn, nvn, la);
  136. Tcl_DecrRefCount (la);
  137. break;
  138. }
  139. break;
  140. case WT_BFS:
  141. switch (order)
  142. {
  143. case WO_BOTH:
  144. la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
  145. lb = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (lb);
  146. res = t_walkbfsboth (interp, tdn, f, cs, avn, nvn, la, lb);
  147. Tcl_DecrRefCount (la);
  148. Tcl_DecrRefCount (lb);
  149. break;
  150. case WO_PRE:
  151. la = Tcl_NewStringObj ("enter",-1); Tcl_IncrRefCount (la);
  152. res = t_walkbfspre (interp, tdn, f, cs, avn, nvn, la);
  153. Tcl_DecrRefCount (la);
  154. break;
  155. case WO_POST:
  156. la = Tcl_NewStringObj ("leave",-1); Tcl_IncrRefCount (la);
  157. res = t_walkbfspost (interp, tdn, f, cs, avn, nvn, la);
  158. Tcl_DecrRefCount (la);
  159. break;
  160. }
  161. break;
  162. }
  163. /* Error and Return are passed unchanged. Everything else is ok */
  164. if (res == TCL_ERROR) {return res;}
  165. if (res == TCL_RETURN) {return res;}
  166. return TCL_OK;
  167. }
  168. /* .................................................. */
  169. int
  170. t_walk_invokescript (Tcl_Interp* interp, TN* n, Tcl_Obj* cs,
  171. Tcl_Obj* avn, Tcl_Obj* nvn,
  172. Tcl_Obj* action)
  173. {
  174. int res;
  175. /* Note: Array elements, like 'a(x)', are not possible as iterator variables */
  176. if (avn) {
  177. Tcl_ObjSetVar2 (interp, avn, NULL, action, 0);
  178. }
  179. Tcl_ObjSetVar2 (interp, nvn, NULL, n->name, 0);
  180. res = Tcl_EvalObj(interp, cs);
  181. return res;
  182. }
  183. int
  184. t_walk_invokecmd (Tcl_Interp* interp, TN* n, Tcl_Obj* dummy0,
  185. Tcl_Obj* dummy1, Tcl_Obj* dummy2,
  186. Tcl_Obj* action)
  187. {
  188. int res;
  189. int cc = (int) dummy0;
  190. Tcl_Obj** ev = (Tcl_Obj**) dummy1; /* cc+3 elements */
  191. ev [cc] = dummy2; /* Tree */
  192. ev [cc+1] = n->name; /* Node */
  193. ev [cc+2] = action; /* Action */
  194. Tcl_IncrRefCount (ev [cc]);
  195. Tcl_IncrRefCount (ev [cc+1]);
  196. Tcl_IncrRefCount (ev [cc+2]);
  197. res = Tcl_EvalObjv (interp, cc+3, ev, 0);
  198. Tcl_DecrRefCount (ev [cc]);
  199. Tcl_DecrRefCount (ev [cc+1]);
  200. Tcl_DecrRefCount (ev [cc+2]);
  201. return res;
  202. }
  203. /* .................................................. */
  204. static int
  205. t_walkdfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  206. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  207. Tcl_Obj* action)
  208. {
  209. /* ok - next node
  210. * error - abort walking
  211. * break - abort walking
  212. * continue - next node
  213. * return - abort walking
  214. * prune /5 - skip children, otherwise ok.
  215. */
  216. int res;
  217. /* Parent before children, action is 'enter'. */
  218. res = (*f) (interp, tdn, cs, avn, nvn, action);
  219. if (res == 5) {
  220. return TCL_OK;
  221. } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
  222. return res;
  223. }
  224. if (tdn->nchildren) {
  225. /* We make a copy of the child array. This emulates the behaviour of
  226. * the Tcl implementation, which will walk to a child of this node,
  227. * even if the loop body/procedure moved it to a different node before
  228. * it was reached by the loop here. If the node it the child is moved
  229. * to was already visited nothing else will happen. Ortherwise the
  230. * child will be visited multiple times.
  231. */
  232. int i;
  233. int nc = tdn->nchildren;
  234. TN** nv = NALLOC (nc,TN*);
  235. memcpy (nv, tdn->child, nc*sizeof(TN*));
  236. for (i = 0; i < nc; i++) {
  237. res = t_walkdfspre (interp, nv [i], f, cs, avn, nvn, action);
  238. /* prune, continue cannot occur, were transformed into ok
  239. * by the child.
  240. */
  241. if (res != TCL_OK) {
  242. ckfree ((char*) nv);
  243. return res;
  244. }
  245. }
  246. ckfree ((char*) nv);
  247. }
  248. return TCL_OK;
  249. }
  250. static int
  251. t_walkdfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  252. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  253. Tcl_Obj* action)
  254. {
  255. int res;
  256. /* Parent after children, action is 'leave'. */
  257. if (tdn->nchildren) {
  258. /* We make a copy of the child array. This emulates the behaviour of
  259. * the Tcl implementation, which will walk to a child of this node,
  260. * even if the loop body/procedure moved it to a different node before
  261. * it was reached by the loop here. If the node it the child is moved
  262. * to was already visited nothing else will happen. Ortherwise the
  263. * child will be visited multiple times.
  264. */
  265. int i;
  266. int nc = tdn->nchildren;
  267. TN** nv = NALLOC (nc,TN*);
  268. memcpy (nv, tdn->child, nc*sizeof(TN*));
  269. for (i = 0; i < nc; i++) {
  270. res = t_walkdfspost (interp, nv [i], f, cs, avn, nvn, action);
  271. if ((res == TCL_ERROR) ||
  272. (res == TCL_BREAK) ||
  273. (res == TCL_RETURN)) {
  274. ckfree ((char*) nv);
  275. return res;
  276. }
  277. }
  278. ckfree ((char*) nv);
  279. }
  280. res = (*f) (interp, tdn, cs, avn, nvn, action);
  281. if ((res == TCL_ERROR) ||
  282. (res == TCL_BREAK) ||
  283. (res == TCL_RETURN)) {
  284. return res;
  285. } else if (res == 5) {
  286. /* Illegal pruning */
  287. Tcl_ResetResult (interp);
  288. Tcl_AppendResult (interp,
  289. "Illegal attempt to prune post-order walking", NULL);
  290. return TCL_ERROR;
  291. }
  292. return TCL_OK;
  293. }
  294. static int
  295. t_walkdfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  296. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  297. Tcl_Obj* enter, Tcl_Obj* leave)
  298. {
  299. /* ok - next node
  300. * error - abort walking
  301. * break - abort walking
  302. * continue - next node
  303. * return - abort walking
  304. * prune /5 - skip children, otherwise ok.
  305. */
  306. int res;
  307. /* Parent before and after Children, action is 'enter' & 'leave'. */
  308. res = (*f) (interp, tdn, cs, avn, nvn, enter);
  309. if (res != 5) {
  310. if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
  311. return res;
  312. }
  313. if (tdn->nchildren) {
  314. int i;
  315. int nc = tdn->nchildren;
  316. TN** nv = NALLOC (nc,TN*);
  317. memcpy (nv, tdn->child, nc*sizeof(TN*));
  318. for (i = 0; i < nc; i++) {
  319. res = t_walkdfsboth (interp, nv [i], f, cs, avn, nvn, enter, leave);
  320. /* prune, continue cannot occur, were transformed into ok
  321. * by the child.
  322. */
  323. if (res != TCL_OK) {
  324. ckfree ((char*) nv);
  325. return res;
  326. }
  327. }
  328. ckfree ((char*) nv);
  329. }
  330. }
  331. res = (*f) (interp, tdn, cs, avn, nvn, leave);
  332. if (res == 5) {
  333. return TCL_OK;
  334. } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
  335. return res;
  336. }
  337. return TCL_OK;
  338. }
  339. static int
  340. t_walkdfsin (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  341. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  342. Tcl_Obj* action)
  343. {
  344. int res;
  345. /* First child visited first, then parent, then */
  346. /* the remaining children. Action is 'visit'. */
  347. /* This is the correct thing for binary trees. */
  348. /* For #children <= 1 the parent is visited */
  349. /* before the child */
  350. if (tdn->nchildren == 0) {
  351. res = (*f) (interp, tdn, cs, avn, nvn, action);
  352. if ((res == TCL_ERROR) ||
  353. (res == TCL_BREAK) ||
  354. (res == TCL_RETURN)) {
  355. return res;
  356. } else if (res == 5) {
  357. /* Illegal pruning */
  358. Tcl_ResetResult (interp);
  359. Tcl_AppendResult (interp,
  360. "Illegal attempt to prune in-order walking", NULL);
  361. return TCL_ERROR;
  362. }
  363. } else if (tdn->nchildren == 1) {
  364. res = (*f) (interp, tdn, cs, avn, nvn, action);
  365. if ((res == TCL_ERROR) ||
  366. (res == TCL_BREAK) ||
  367. (res == TCL_RETURN)) {
  368. return res;
  369. } else if (res == 5) {
  370. /* Illegal pruning */
  371. Tcl_ResetResult (interp);
  372. Tcl_AppendResult (interp,
  373. "Illegal attempt to prune in-order walking", NULL);
  374. return TCL_ERROR;
  375. }
  376. return t_walkdfsin (interp, tdn->child [0], f, cs, avn, nvn, action);
  377. } else {
  378. int i;
  379. int nc = tdn->nchildren;
  380. TN** nv = NALLOC (nc,TN*);
  381. memcpy (nv, tdn->child, nc*sizeof(TN*));
  382. res = t_walkdfsin (interp, tdn->child [0], f, cs, avn, nvn, action);
  383. if ((res == TCL_ERROR) ||
  384. (res == TCL_BREAK) ||
  385. (res == TCL_RETURN)) {
  386. ckfree ((char*) nv);
  387. return res;
  388. }
  389. res = (*f) (interp, tdn, cs, avn, nvn, action);
  390. if ((res == TCL_ERROR) ||
  391. (res == TCL_BREAK) ||
  392. (res == TCL_RETURN)) {
  393. ckfree ((char*) nv);
  394. return res;
  395. } else if (res == 5) {
  396. /* Illegal pruning */
  397. ckfree ((char*) nv);
  398. Tcl_ResetResult (interp);
  399. Tcl_AppendResult (interp,
  400. "Illegal attempt to prune in-order walking", NULL);
  401. return TCL_ERROR;
  402. }
  403. for (i = 1; i < nc; i++) {
  404. res = t_walkdfsin (interp, nv [i], f, cs, avn, nvn, action);
  405. if ((res == TCL_ERROR) ||
  406. (res == TCL_BREAK) ||
  407. (res == TCL_RETURN)) {
  408. ckfree ((char*) nv);
  409. return res;
  410. }
  411. }
  412. ckfree ((char*) nv);
  413. }
  414. return TCL_OK;
  415. }
  416. static int
  417. t_walkbfsboth (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  418. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  419. Tcl_Obj* enter, Tcl_Obj* leave)
  420. {
  421. /* ok - next node
  422. * error - abort walking
  423. * break - pre: abort walking, skip to post, post: abort walking
  424. * continue - next node
  425. * return - abort walking
  426. * prune /5 - skip children, otherwise ok.
  427. */
  428. int res;
  429. TN* n;
  430. NLQ q;
  431. NLQ qb;
  432. nlq_init (&q);
  433. nlq_init (&qb);
  434. nlq_append (&q, tdn);
  435. nlq_push (&qb, tdn);
  436. while (1) {
  437. n = nlq_pop (&q);
  438. if (!n) break;
  439. res = (*f) (interp, n, cs, avn, nvn, enter);
  440. if (res == 5) {
  441. continue;
  442. } else if (res == TCL_ERROR) {
  443. nlq_clear (&q);
  444. nlq_clear (&qb);
  445. return res;
  446. } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
  447. nlq_clear (&q);
  448. /* We abort the collection of more nodes, but still run the
  449. * backward iteration (post-order phase).
  450. */
  451. break;
  452. }
  453. if (n->nchildren) {
  454. int i;
  455. for (i = 0; i < n->nchildren; i++) {
  456. nlq_append (&q, n->child [i]);
  457. nlq_push (&qb, n->child [i]);
  458. }
  459. }
  460. }
  461. /* Backward visit to leave */
  462. while (1) {
  463. n = nlq_pop (&qb);
  464. if (!n) break;
  465. res = (*f) (interp, n, cs, avn, nvn, leave);
  466. if (res == 5) {
  467. continue;
  468. } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
  469. nlq_clear (&qb);
  470. return res;
  471. }
  472. }
  473. return TCL_OK;
  474. }
  475. static int
  476. t_walkbfspre (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  477. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  478. Tcl_Obj* action)
  479. {
  480. /* ok - next node
  481. * error - abort walking
  482. * break - abort walking
  483. * continue - next node
  484. * return - abort walking
  485. * prune /5 - skip children, otherwise ok.
  486. */
  487. int res;
  488. TN* n;
  489. NLQ q;
  490. nlq_init (&q);
  491. nlq_append (&q, tdn);
  492. while (1) {
  493. n = nlq_pop (&q);
  494. if (!n) break;
  495. res = (*f) (interp, n, cs, avn, nvn, action);
  496. if (res == 5) {
  497. continue;
  498. } else if ((res != TCL_OK) && (res != TCL_CONTINUE)) {
  499. nlq_clear (&q);
  500. return res;
  501. }
  502. if (n->nchildren) {
  503. int i;
  504. for (i = 0; i < n->nchildren; i++) {
  505. nlq_append (&q, n->child [i]);
  506. }
  507. }
  508. }
  509. return TCL_OK;
  510. }
  511. static int
  512. t_walkbfspost (Tcl_Interp* interp, TN* tdn, t_walk_function f,
  513. Tcl_Obj* cs, Tcl_Obj* avn, Tcl_Obj* nvn,
  514. Tcl_Obj* action)
  515. {
  516. int res;
  517. TN* n;
  518. NLQ q;
  519. NLQ qb;
  520. nlq_init (&q);
  521. nlq_init (&qb);
  522. nlq_append (&q, tdn);
  523. nlq_push (&qb, tdn);
  524. while (1) {
  525. n = nlq_pop (&q);
  526. if (!n) break;
  527. if (n->nchildren) {
  528. int i;
  529. for (i = 0; i < n->nchildren; i++) {
  530. nlq_append (&q, n->child [i]);
  531. nlq_push (&qb, n->child [i]);
  532. }
  533. }
  534. }
  535. /* Backward visit to leave */
  536. while (1) {
  537. n = nlq_pop (&qb);
  538. if (!n) break;
  539. res = (*f) (interp, n, cs, avn, nvn, action);
  540. if ((res == TCL_ERROR) ||
  541. (res == TCL_BREAK) ||
  542. (res == TCL_RETURN)) {
  543. nlq_clear (&qb);
  544. return res;
  545. } else if (res == 5) {
  546. /* Illegal pruning */
  547. nlq_clear (&qb);
  548. Tcl_ResetResult (interp);
  549. Tcl_AppendResult (interp,
  550. "Illegal attempt to prune post-order walking", NULL);
  551. return TCL_ERROR;
  552. }
  553. }
  554. return TCL_OK;
  555. }
  556. /*
  557. * Local Variables:
  558. * mode: c
  559. * c-basic-offset: 4
  560. * fill-column: 78
  561. * End:
  562. */