trees.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /* Copyright 1995-2010,2018
  2. Free Software Foundation, Inc.
  3. This file is part of Guile.
  4. Guile is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Lesser General Public License as published
  6. by the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. Guile is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  11. License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with Guile. If not, see
  14. <https://www.gnu.org/licenses/>. */
  15. #ifdef HAVE_CONFIG_H
  16. # include <config.h>
  17. #endif
  18. #include <stdarg.h>
  19. #include "eq.h"
  20. #include "gsubr.h"
  21. #include "list.h"
  22. #include "pairs.h"
  23. #include "srcprop.h"
  24. #include "vectors.h"
  25. #include "trees.h"
  26. /* scm_copy_tree creates deep copies of pairs and vectors, but not of any other
  27. * data types.
  28. *
  29. * To avoid infinite recursion due to cyclic structures, the hare-and-tortoise
  30. * pattern is used to detect cycles. In fact, the pattern is used in two
  31. * dimensions, vertical (indicated in the code by the variable names 'hare'
  32. * and 'tortoise') and horizontal ('rabbit' and 'turtle'). In both
  33. * dimensions, the hare/rabbit will take two steps when the tortoise/turtle
  34. * takes one.
  35. *
  36. * The vertical dimension corresponds to recursive calls to function
  37. * copy_tree: This happens when descending into vector elements, into cars of
  38. * lists and into the cdr of an improper list. In this dimension, the
  39. * tortoise follows the hare by using the processor stack: Every stack frame
  40. * will hold an instance of struct t_trace. These instances are connected in
  41. * a way that represents the trace of the hare, which thus can be followed by
  42. * the tortoise. The tortoise will always point to struct t_trace instances
  43. * relating to SCM objects that have already been copied. Thus, a cycle is
  44. * detected if the tortoise and the hare point to the same object,
  45. *
  46. * The horizontal dimension is within one execution of copy_tree, when the
  47. * function cdr's along the pairs of a list. This is the standard
  48. * hare-and-tortoise implementation, found several times in guile. */
  49. struct t_trace {
  50. struct t_trace *trace; /* These pointers form a trace along the stack. */
  51. SCM obj; /* The object handled at the respective stack frame.*/
  52. };
  53. static SCM
  54. copy_tree (struct t_trace *const hare,
  55. struct t_trace *tortoise,
  56. unsigned int tortoise_delay);
  57. SCM_DEFINE (scm_copy_tree, "copy-tree", 1, 0, 0,
  58. (SCM obj),
  59. "Recursively copy the data tree that is bound to @var{obj}, and return a\n"
  60. "the new data structure. @code{copy-tree} recurses down the\n"
  61. "contents of both pairs and vectors (since both cons cells and vector\n"
  62. "cells may point to arbitrary objects), and stops recursing when it hits\n"
  63. "any other object.")
  64. #define FUNC_NAME s_scm_copy_tree
  65. {
  66. /* Prepare the trace along the stack. */
  67. struct t_trace trace;
  68. trace.obj = obj;
  69. /* In function copy_tree, if the tortoise makes its step, it will do this
  70. * before the hare has the chance to move. Thus, we have to make sure that
  71. * the very first step of the tortoise will not happen after the hare has
  72. * really made two steps. This is achieved by passing '2' as the initial
  73. * delay for the tortoise. NOTE: Since cycles are unlikely, giving the hare
  74. * a bigger advantage may improve performance slightly. */
  75. return copy_tree (&trace, &trace, 2);
  76. }
  77. #undef FUNC_NAME
  78. static SCM
  79. copy_tree (struct t_trace *const hare,
  80. struct t_trace *tortoise,
  81. unsigned int tortoise_delay)
  82. #define FUNC_NAME s_scm_copy_tree
  83. {
  84. if (!scm_is_pair (hare->obj) && !scm_is_vector (hare->obj))
  85. {
  86. return hare->obj;
  87. }
  88. else
  89. {
  90. /* Prepare the trace along the stack. */
  91. struct t_trace new_hare;
  92. hare->trace = &new_hare;
  93. /* The tortoise will make its step after the delay has elapsed. Note
  94. * that in contrast to the typical hare-and-tortoise pattern, the step
  95. * of the tortoise happens before the hare takes its steps. This is, in
  96. * principle, no problem, except for the start of the algorithm: Then,
  97. * it has to be made sure that the hare actually gets its advantage of
  98. * two steps. */
  99. if (tortoise_delay == 0)
  100. {
  101. tortoise_delay = 1;
  102. tortoise = tortoise->trace;
  103. if (SCM_UNLIKELY (scm_is_eq (hare->obj, tortoise->obj)))
  104. scm_wrong_type_arg_msg (FUNC_NAME, 1, hare->obj,
  105. "expected non-circular data structure");
  106. }
  107. else
  108. {
  109. --tortoise_delay;
  110. }
  111. if (scm_is_vector (hare->obj))
  112. {
  113. size_t length = SCM_VECTOR_LENGTH (hare->obj);
  114. SCM new_vector = scm_c_make_vector (length, SCM_UNSPECIFIED);
  115. /* Each vector element is copied by recursing into copy_tree, having
  116. * the tortoise follow the hare into the depths of the stack. */
  117. unsigned long int i;
  118. for (i = 0; i < length; ++i)
  119. {
  120. SCM new_element;
  121. new_hare.obj = SCM_VECTOR_REF (hare->obj, i);
  122. new_element = copy_tree (&new_hare, tortoise, tortoise_delay);
  123. SCM_VECTOR_SET (new_vector, i, new_element);
  124. }
  125. return new_vector;
  126. }
  127. else /* scm_is_pair (hare->obj) */
  128. {
  129. SCM result;
  130. SCM tail;
  131. SCM rabbit = hare->obj;
  132. SCM turtle = hare->obj;
  133. SCM copy;
  134. /* The first pair of the list is treated specially, in order to
  135. * preserve a potential source code position. */
  136. result = tail = scm_cons_source (rabbit, SCM_EOL, SCM_EOL);
  137. new_hare.obj = SCM_CAR (rabbit);
  138. copy = copy_tree (&new_hare, tortoise, tortoise_delay);
  139. SCM_SETCAR (tail, copy);
  140. /* The remaining pairs of the list are copied by, horizontally,
  141. * having the turtle follow the rabbit, and, vertically, having the
  142. * tortoise follow the hare into the depths of the stack. */
  143. rabbit = SCM_CDR (rabbit);
  144. while (scm_is_pair (rabbit))
  145. {
  146. new_hare.obj = SCM_CAR (rabbit);
  147. copy = copy_tree (&new_hare, tortoise, tortoise_delay);
  148. SCM_SETCDR (tail, scm_cons (copy, SCM_UNDEFINED));
  149. tail = SCM_CDR (tail);
  150. rabbit = SCM_CDR (rabbit);
  151. if (scm_is_pair (rabbit))
  152. {
  153. new_hare.obj = SCM_CAR (rabbit);
  154. copy = copy_tree (&new_hare, tortoise, tortoise_delay);
  155. SCM_SETCDR (tail, scm_cons (copy, SCM_UNDEFINED));
  156. tail = SCM_CDR (tail);
  157. rabbit = SCM_CDR (rabbit);
  158. turtle = SCM_CDR (turtle);
  159. if (SCM_UNLIKELY (scm_is_eq (rabbit, turtle)))
  160. scm_wrong_type_arg_msg (FUNC_NAME, 1, rabbit,
  161. "expected non-circular data structure");
  162. }
  163. }
  164. /* We have to recurse into copy_tree again for the last cdr, in
  165. * order to handle the situation that it holds a vector. */
  166. new_hare.obj = rabbit;
  167. copy = copy_tree (&new_hare, tortoise, tortoise_delay);
  168. SCM_SETCDR (tail, copy);
  169. return result;
  170. }
  171. }
  172. }
  173. #undef FUNC_NAME
  174. void
  175. scm_init_trees ()
  176. {
  177. #include "trees.x"
  178. }