trees.c 7.7 KB

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