procedures-with-setters implemented in terms of structs
[bpt/guile.git] / libguile / struct.c
CommitLineData
f86f3b5b 1/* Copyright (C) 1996,1997,1998,1999,2000,2001, 2003, 2004, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
0f2d19dd 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
0f2d19dd 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
73be1d9e 17 */
1bbd0b84 18
0f2d19dd 19\f
dbb605f5 20#ifdef HAVE_CONFIG_H
a6f7f57d
RB
21# include <config.h>
22#endif
0f2d19dd 23
a0599745 24#include "libguile/_scm.h"
4e047c3e 25#include "libguile/async.h"
a0599745
MD
26#include "libguile/chars.h"
27#include "libguile/eval.h"
28#include "libguile/alist.h"
29#include "libguile/weaks.h"
30#include "libguile/hashtab.h"
31#include "libguile/ports.h"
32#include "libguile/strings.h"
27646f41 33#include "libguile/srfi-13.h"
a0599745
MD
34
35#include "libguile/validate.h"
36#include "libguile/struct.h"
0f2d19dd 37
d15ad007
LC
38#include "libguile/eq.h"
39
95b88819
GH
40#ifdef HAVE_STRING_H
41#include <string.h>
42#endif
43
1c44468d 44#include "libguile/bdw-gc.h"
5e67dc27 45
0f2d19dd
JB
46\f
47
b6cf4d02
AW
48/* A needlessly obscure test. */
49#define SCM_LAYOUT_TAILP(X) (((X) & 32) == 0) /* R, W or O */
50
0f2d19dd 51static SCM required_vtable_fields = SCM_BOOL_F;
b6cf4d02
AW
52static SCM required_applicable_fields = SCM_BOOL_F;
53static SCM required_applicable_with_setter_fields = SCM_BOOL_F;
54SCM scm_struct_table = SCM_BOOL_F;
0f2d19dd
JB
55
56\f
a1ec6916 57SCM_DEFINE (scm_make_struct_layout, "make-struct-layout", 1, 0, 0,
1bbd0b84 58 (SCM fields),
b380b885 59 "Return a new structure layout object.\n\n"
7c31152f 60 "@var{fields} must be a string made up of pairs of characters\n"
b380b885
MD
61 "strung together. The first character of each pair describes a field\n"
62 "type, the second a field protection. Allowed types are 'p' for\n"
63 "GC-protected Scheme data, 'u' for unprotected binary data, and 's' for\n"
04323af4 64 "a field that points to the structure itself. Allowed protections\n"
b6cf4d02
AW
65 "are 'w' for mutable fields, 'h' for hidden fields, 'r' for read-only\n"
66 "fields, and 'o' for opaque fields.\n\n"
67 "Hidden fields are writable, but they will not consume an initializer arg\n"
68 "passed to @code{make-struct}. They are useful to add slots to a struct\n"
69 "in a way that preserves backward-compatibility with existing calls to\n"
70 "@code{make-struct}, especially for derived vtables.\n\n"
71 "The last field protection specification may be capitalized to indicate\n"
72 "that the field is a tail-array.")
1bbd0b84 73#define FUNC_NAME s_scm_make_struct_layout
0f2d19dd
JB
74{
75 SCM new_sym;
27646f41 76 scm_t_wchar c;
2ade72d7 77
7f991c7d
LC
78 SCM_VALIDATE_STRING (1, fields);
79
1bbd0b84 80 { /* scope */
1be6b49c 81 size_t len;
0f2d19dd
JB
82 int x;
83
cc95e00a 84 len = scm_i_string_length (fields);
2ade72d7
DH
85 if (len % 2 == 1)
86 SCM_MISC_ERROR ("odd length field specification: ~S",
1afff620 87 scm_list_1 (fields));
2ade72d7 88
0f2d19dd
JB
89 for (x = 0; x < len; x += 2)
90 {
27646f41 91 switch (c = scm_i_string_ref (fields, x))
0f2d19dd
JB
92 {
93 case 'u':
94 case 'p':
95#if 0
96 case 'i':
97 case 'd':
98#endif
99 case 's':
100 break;
101 default:
2ade72d7 102 SCM_MISC_ERROR ("unrecognized field type: ~S",
27646f41 103 scm_list_1 (SCM_MAKE_CHAR (c)));
0f2d19dd
JB
104 }
105
27646f41 106 switch (c = scm_i_string_ref (fields, x + 1))
0f2d19dd
JB
107 {
108 case 'w':
b6cf4d02 109 case 'h':
27646f41 110 if (scm_i_string_ref (fields, x) == 's')
2ade72d7 111 SCM_MISC_ERROR ("self fields not writable", SCM_EOL);
0f2d19dd
JB
112 case 'r':
113 case 'o':
114 break;
2c36c351
MD
115 case 'R':
116 case 'W':
117 case 'O':
27646f41 118 if (scm_i_string_ref (fields, x) == 's')
2ade72d7
DH
119 SCM_MISC_ERROR ("self fields not allowed in tail array",
120 SCM_EOL);
121 if (x != len - 2)
122 SCM_MISC_ERROR ("tail array field must be last field in layout",
123 SCM_EOL);
2c36c351 124 break;
0f2d19dd 125 default:
2ade72d7 126 SCM_MISC_ERROR ("unrecognized ref specification: ~S",
27646f41 127 scm_list_1 (SCM_MAKE_CHAR (c)));
0f2d19dd
JB
128 }
129#if 0
27646f41 130 if (scm_i_string_ref (fields, x, 'd'))
0f2d19dd 131 {
27646f41 132 if (!scm_i_string_ref (fields, x+2, '-'))
2ade72d7 133 SCM_MISC_ERROR ("missing dash field at position ~A",
e11e83f3 134 scm_list_1 (scm_from_int (x / 2)));
0f2d19dd
JB
135 x += 2;
136 goto recheck_ref;
137 }
138#endif
139 }
cc95e00a 140 new_sym = scm_string_to_symbol (fields);
0f2d19dd 141 }
8824ac88
MV
142 scm_remember_upto_here_1 (fields);
143 return new_sym;
0f2d19dd 144}
1bbd0b84 145#undef FUNC_NAME
0f2d19dd
JB
146
147\f
148
51f66c91
AW
149void
150scm_i_struct_inherit_vtable_magic (SCM vtable, SCM obj)
151#define FUNC_NAME "%inherit-vtable-magic"
152{
153 /* Verily, what is the deal here, you ask? Basically, we need to know a couple
154 of properties of structures at runtime. For example, "is this structure a
155 vtable of vtables (a metaclass)?"; also, "is this structure applicable?".
156 Both of these questions also imply a certain layout of the structure. So
157 instead of checking the layout at runtime, what we do is pre-verify the
158 layout -- so that at runtime we can just check the applicable flag and
159 dispatch directly to the Scheme procedure in slot 0.
160 */
161 SCM olayout;
162
163 /* verify that obj is a valid vtable */
164 if (scm_is_false (scm_symbol_p (SCM_VTABLE_LAYOUT (obj))))
165 scm_misc_error (FUNC_NAME, "invalid layout for new vtable",
166 scm_list_1 (SCM_VTABLE_LAYOUT (obj)));
167
168 /* if obj's vtable is compatible with the required vtable (class) layout, it
169 is a metaclass */
170 olayout = scm_symbol_to_string (SCM_VTABLE_LAYOUT (obj));
171 if (scm_is_true (scm_leq_p (scm_string_length (required_vtable_fields),
172 scm_string_length (olayout)))
173 && scm_is_true (scm_string_eq (olayout, required_vtable_fields,
174 scm_from_size_t (0),
175 scm_string_length (required_vtable_fields),
176 scm_from_size_t (0),
177 scm_string_length (required_vtable_fields))))
178 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_VTABLE);
179
180 /* finally if obj is an applicable class, verify that its vtable is
181 compatible with the required applicable layout */
182 if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SETTER_VTABLE))
183 {
184 if (scm_is_false (scm_string_eq (olayout, required_applicable_with_setter_fields,
185 scm_from_size_t (0),
186 scm_from_size_t (4),
187 scm_from_size_t (0),
188 scm_from_size_t (4))))
189 scm_misc_error (FUNC_NAME, "invalid applicable-with-setter struct layout",
190 scm_list_1 (olayout));
191 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_APPLICABLE | SCM_VTABLE_FLAG_SETTER);
192 }
193 else if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_APPLICABLE_VTABLE))
194 {
195 if (scm_is_false (scm_string_eq (olayout, required_applicable_fields,
196 scm_from_size_t (0),
197 scm_from_size_t (2),
198 scm_from_size_t (0),
199 scm_from_size_t (2))))
200 scm_misc_error (FUNC_NAME, "invalid applicable struct layout",
201 scm_list_1 (olayout));
202 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_APPLICABLE);
203 }
204}
205#undef FUNC_NAME
0f2d19dd 206
1cc91f1b 207
f7620510 208static void
b6cf4d02 209scm_struct_init (SCM handle, SCM layout, int tail_elts, SCM inits)
0f2d19dd 210{
27646f41 211 scm_t_wchar prot = 0;
cc95e00a 212 int n_fields = scm_i_symbol_length (layout) / 2;
2c36c351 213 int tailp = 0;
27646f41 214 int i;
b6cf4d02 215 scm_t_bits *mem = SCM_STRUCT_DATA (handle);
d8c40b9f 216
27646f41 217 i = -2;
0f2d19dd
JB
218 while (n_fields)
219 {
2c36c351
MD
220 if (!tailp)
221 {
27646f41
MG
222 i += 2;
223 prot = scm_i_symbol_ref (layout, i+1);
2c36c351
MD
224 if (SCM_LAYOUT_TAILP (prot))
225 {
226 tailp = 1;
227 prot = prot == 'R' ? 'r' : prot == 'W' ? 'w' : 'o';
d8c40b9f 228 *mem++ = tail_elts;
2c36c351
MD
229 n_fields += tail_elts - 1;
230 if (n_fields == 0)
231 break;
232 }
233 }
27646f41 234 switch (scm_i_symbol_ref (layout, i))
0f2d19dd
JB
235 {
236#if 0
237 case 'i':
2c36c351 238 if ((prot != 'r' && prot != 'w') || inits == SCM_EOL)
0f2d19dd
JB
239 *mem = 0;
240 else
241 {
b9bd8526 242 *mem = scm_to_long (SCM_CAR (inits));
0f2d19dd
JB
243 inits = SCM_CDR (inits);
244 }
245 break;
246#endif
247
248 case 'u':
d2e53ed6 249 if ((prot != 'r' && prot != 'w') || scm_is_null (inits))
0f2d19dd
JB
250 *mem = 0;
251 else
252 {
b9bd8526 253 *mem = scm_to_ulong (SCM_CAR (inits));
0f2d19dd
JB
254 inits = SCM_CDR (inits);
255 }
256 break;
257
258 case 'p':
d2e53ed6 259 if ((prot != 'r' && prot != 'w') || scm_is_null (inits))
d8c40b9f 260 *mem = SCM_UNPACK (SCM_BOOL_F);
0f2d19dd
JB
261 else
262 {
d8c40b9f 263 *mem = SCM_UNPACK (SCM_CAR (inits));
0f2d19dd
JB
264 inits = SCM_CDR (inits);
265 }
266
267 break;
268
269#if 0
270 case 'd':
2c36c351 271 if ((prot != 'r' && prot != 'w') || inits == SCM_EOL)
0f2d19dd
JB
272 *((double *)mem) = 0.0;
273 else
274 {
a5bfe84d 275 *mem = scm_num2dbl (SCM_CAR (inits), "scm_struct_init");
0f2d19dd
JB
276 inits = SCM_CDR (inits);
277 }
278 fields_desc += 2;
279 break;
280#endif
281
282 case 's':
d8c40b9f 283 *mem = SCM_UNPACK (handle);
0f2d19dd
JB
284 break;
285 }
286
0f2d19dd
JB
287 n_fields--;
288 mem++;
289 }
290}
291
292
a1ec6916 293SCM_DEFINE (scm_struct_p, "struct?", 1, 0, 0,
1bbd0b84 294 (SCM x),
0233bfc1 295 "Return @code{#t} iff @var{x} is a structure object, else\n"
942e5b91 296 "@code{#f}.")
1bbd0b84 297#define FUNC_NAME s_scm_struct_p
0f2d19dd 298{
7888309b 299 return scm_from_bool(SCM_STRUCTP (x));
0f2d19dd 300}
1bbd0b84 301#undef FUNC_NAME
0f2d19dd 302
a1ec6916 303SCM_DEFINE (scm_struct_vtable_p, "struct-vtable?", 1, 0, 0,
1bbd0b84 304 (SCM x),
0233bfc1 305 "Return @code{#t} iff @var{x} is a vtable structure.")
1bbd0b84 306#define FUNC_NAME s_scm_struct_vtable_p
0f2d19dd 307{
b6cf4d02
AW
308 return scm_from_bool
309 (SCM_STRUCTP (x)
310 && SCM_STRUCT_VTABLE_FLAG_IS_SET (x, SCM_VTABLE_FLAG_VTABLE));
0f2d19dd 311}
1bbd0b84 312#undef FUNC_NAME
0f2d19dd 313
14d1400f 314
51f66c91
AW
315/* Finalization: invoke the finalizer of the struct pointed to by PTR. */
316static void
317struct_finalizer_trampoline (GC_PTR ptr, GC_PTR unused_data)
318{
319 SCM obj = PTR2SCM (ptr);
320 scm_t_struct_finalize finalize = SCM_STRUCT_FINALIZER (obj);
321
322 if (finalize)
323 finalize (obj);
324}
325
14d1400f
JB
326/* All struct data must be allocated at an address whose bottom three
327 bits are zero. This is because the tag for a struct lives in the
328 bottom three bits of the struct's car, and the upper bits point to
329 the data of its vtable, which is a struct itself. Thus, if the
330 address of that data doesn't end in three zeros, tagging it will
331 destroy the pointer.
332
b6cf4d02
AW
333 I suppose we should make it clear here that, the data must be 8-byte aligned,
334 *within* the struct, and the struct itself should be 8-byte aligned. In
335 practice we ensure this because the data starts two words into a struct.
14d1400f 336
b6cf4d02
AW
337 This function allocates an 8-byte aligned block of memory, whose first word
338 points to the given vtable data, then a data pointer, then n_words of data.
339 */
340SCM
51f66c91 341scm_i_alloc_struct (scm_t_bits *vtable_data, int n_words, const char *what)
14d1400f 342{
b6cf4d02
AW
343 scm_t_bits ret;
344 ret = (scm_t_bits)scm_gc_malloc (sizeof (scm_t_bits) * (n_words + 2), "struct");
b6cf4d02
AW
345 SCM_SET_CELL_WORD_0 (SCM_PACK (ret), (scm_t_bits)vtable_data | scm_tc3_struct);
346 SCM_SET_CELL_WORD_1 (SCM_PACK (ret),
347 (scm_t_bits)SCM_CELL_OBJECT_LOC (SCM_PACK (ret), 2));
5e67dc27 348
51f66c91
AW
349 /* vtable_data can be null when making a vtable vtable */
350 if (vtable_data && vtable_data[scm_vtable_index_instance_finalize])
351 {
352 /* Register a finalizer for the newly created instance. */
353 GC_finalization_proc prev_finalizer;
354 GC_PTR prev_finalizer_data;
355 GC_REGISTER_FINALIZER_NO_ORDER ((void*)ret,
356 struct_finalizer_trampoline,
357 NULL,
358 &prev_finalizer,
359 &prev_finalizer_data);
360 }
5e67dc27 361
51f66c91 362 return SCM_PACK (ret);
5e67dc27
LC
363}
364
5e67dc27 365\f
a1ec6916 366SCM_DEFINE (scm_make_struct, "make-struct", 2, 0, 1,
1bbd0b84 367 (SCM vtable, SCM tail_array_size, SCM init),
b380b885 368 "Create a new structure.\n\n"
1bee0e70 369 "@var{type} must be a vtable structure (@pxref{Vtables}).\n\n"
b380b885
MD
370 "@var{tail-elts} must be a non-negative integer. If the layout\n"
371 "specification indicated by @var{type} includes a tail-array,\n"
372 "this is the number of elements allocated to that array.\n\n"
6386e25c 373 "The @var{init1}, @dots{} are optional arguments describing how\n"
04323af4
MD
374 "successive fields of the structure should be initialized. Only fields\n"
375 "with protection 'r' or 'w' can be initialized, except for fields of\n"
376 "type 's', which are automatically initialized to point to the new\n"
b6cf4d02 377 "structure itself. Fields with protection 'o' can not be initialized by\n"
04323af4
MD
378 "Scheme programs.\n\n"
379 "If fewer optional arguments than initializable fields are supplied,\n"
380 "fields of type 'p' get default value #f while fields of type 'u' are\n"
381 "initialized to 0.\n\n"
6386e25c 382 "For more information, see the documentation for @code{make-vtable-vtable}.")
1bbd0b84 383#define FUNC_NAME s_scm_make_struct
0f2d19dd
JB
384{
385 SCM layout;
a55c2b68
MV
386 size_t basic_size;
387 size_t tail_elts;
b6cf4d02 388 SCM obj;
0f2d19dd 389
34d19ef6 390 SCM_VALIDATE_VTABLE (1, vtable);
af45e3b0 391 SCM_VALIDATE_REST_ARGUMENT (init);
0f2d19dd 392
b6cf4d02 393 layout = SCM_VTABLE_LAYOUT (vtable);
cc95e00a 394 basic_size = scm_i_symbol_length (layout) / 2;
a55c2b68 395 tail_elts = scm_to_size_t (tail_array_size);
651f2cd2
KR
396
397 /* A tail array is only allowed if the layout fields string ends in "R",
398 "W" or "O". */
399 if (tail_elts != 0)
400 {
401 SCM layout_str, last_char;
402
403 if (basic_size == 0)
404 {
405 bad_tail:
406 SCM_MISC_ERROR ("tail array not allowed unless layout ends R, W, or O", SCM_EOL);
407 }
408
409 layout_str = scm_symbol_to_string (layout);
410 last_char = scm_string_ref (layout_str,
411 scm_from_size_t (2 * basic_size - 1));
412 if (! SCM_LAYOUT_TAILP (SCM_CHAR (last_char)))
413 goto bad_tail;
414 }
cb823e63 415
51f66c91
AW
416 obj = scm_i_alloc_struct (SCM_STRUCT_DATA (vtable), basic_size + tail_elts,
417 "struct");
5e67dc27 418
b6cf4d02
AW
419 scm_struct_init (obj, layout, tail_elts, init);
420
51f66c91
AW
421 /* only check things and inherit magic if the layout was passed as an initarg.
422 something of a hack, but it's for back-compatibility. */
b6cf4d02 423 if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_VTABLE)
b6cf4d02 424 && scm_is_true (SCM_VTABLE_LAYOUT (obj)))
51f66c91 425 scm_i_struct_inherit_vtable_magic (vtable, obj);
651f2cd2 426
b6cf4d02 427 return obj;
0f2d19dd 428}
1bbd0b84 429#undef FUNC_NAME
0f2d19dd
JB
430
431
432
a1ec6916 433SCM_DEFINE (scm_make_vtable_vtable, "make-vtable-vtable", 2, 0, 1,
04323af4 434 (SCM user_fields, SCM tail_array_size, SCM init),
b380b885 435 "Return a new, self-describing vtable structure.\n\n"
04323af4
MD
436 "@var{user-fields} is a string describing user defined fields of the\n"
437 "vtable beginning at index @code{vtable-offset-user}\n"
438 "(see @code{make-struct-layout}).\n\n"
b380b885
MD
439 "@var{tail-size} specifies the size of the tail-array (if any) of\n"
440 "this vtable.\n\n"
6386e25c 441 "@var{init1}, @dots{} are the optional initializers for the fields of\n"
04323af4
MD
442 "the vtable.\n\n"
443 "Vtables have one initializable system field---the struct printer.\n"
444 "This field comes before the user fields in the initializers passed\n"
445 "to @code{make-vtable-vtable} and @code{make-struct}, and thus works as\n"
446 "a third optional argument to @code{make-vtable-vtable} and a fourth to\n"
447 "@code{make-struct} when creating vtables:\n\n"
448 "If the value is a procedure, it will be called instead of the standard\n"
449 "printer whenever a struct described by this vtable is printed.\n"
450 "The procedure will be called with arguments STRUCT and PORT.\n\n"
451 "The structure of a struct is described by a vtable, so the vtable is\n"
452 "in essence the type of the struct. The vtable is itself a struct with\n"
453 "a vtable. This could go on forever if it weren't for the\n"
29b4f9fb 454 "vtable-vtables which are self-describing vtables, and thus terminate\n"
04323af4
MD
455 "the chain.\n\n"
456 "There are several potential ways of using structs, but the standard\n"
457 "one is to use three kinds of structs, together building up a type\n"
458 "sub-system: one vtable-vtable working as the root and one or several\n"
459 "\"types\", each with a set of \"instances\". (The vtable-vtable should be\n"
29b4f9fb 460 "compared to the class <class> which is the class of itself.)\n\n"
1e6808ea 461 "@lisp\n"
04323af4
MD
462 "(define ball-root (make-vtable-vtable \"pr\" 0))\n\n"
463 "(define (make-ball-type ball-color)\n"
464 " (make-struct ball-root 0\n"
465 " (make-struct-layout \"pw\")\n"
466 " (lambda (ball port)\n"
467 " (format port \"#<a ~A ball owned by ~A>\"\n"
468 " (color ball)\n"
469 " (owner ball)))\n"
470 " ball-color))\n"
471 "(define (color ball) (struct-ref (struct-vtable ball) vtable-offset-user))\n"
472 "(define (owner ball) (struct-ref ball 0))\n\n"
473 "(define red (make-ball-type 'red))\n"
474 "(define green (make-ball-type 'green))\n\n"
475 "(define (make-ball type owner) (make-struct type 0 owner))\n\n"
476 "(define ball (make-ball green 'Nisse))\n"
477 "ball @result{} #<a green ball owned by Nisse>\n"
9401323e 478 "@end lisp")
1bbd0b84 479#define FUNC_NAME s_scm_make_vtable_vtable
0f2d19dd
JB
480{
481 SCM fields;
482 SCM layout;
a55c2b68
MV
483 size_t basic_size;
484 size_t tail_elts;
b6cf4d02 485 SCM obj;
0f2d19dd 486
d1ca2c64 487 SCM_VALIDATE_STRING (1, user_fields);
af45e3b0 488 SCM_VALIDATE_REST_ARGUMENT (init);
0f2d19dd 489
1afff620
KN
490 fields = scm_string_append (scm_list_2 (required_vtable_fields,
491 user_fields));
0f2d19dd 492 layout = scm_make_struct_layout (fields);
cc95e00a 493 basic_size = scm_i_symbol_length (layout) / 2;
a55c2b68 494 tail_elts = scm_to_size_t (tail_array_size);
9de87eea 495 SCM_CRITICAL_SECTION_START;
51f66c91 496 obj = scm_i_alloc_struct (NULL, basic_size + tail_elts, "struct");
b6cf4d02
AW
497 /* magic magic magic */
498 SCM_SET_CELL_WORD_0 (obj, (scm_t_bits)SCM_STRUCT_DATA (obj) | scm_tc3_struct);
9de87eea 499 SCM_CRITICAL_SECTION_END;
b6cf4d02
AW
500 scm_struct_init (obj, layout, tail_elts, scm_cons (layout, init));
501 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_VTABLE);
502 return obj;
0f2d19dd 503}
1bbd0b84 504#undef FUNC_NAME
0f2d19dd 505
d15ad007 506
651f2cd2
KR
507static SCM scm_i_vtable_vtable_no_extra_fields;
508
509SCM_DEFINE (scm_make_vtable, "make-vtable", 1, 1, 0,
510 (SCM fields, SCM printer),
511 "Create a vtable, for creating structures with the given\n"
512 "@var{fields}.\n"
513 "\n"
514 "The optional @var{printer} argument is a function to be called\n"
515 "@code{(@var{printer} struct port)} on the structures created.\n"
516 "It should look at @var{struct} and write to @var{port}.")
517#define FUNC_NAME s_scm_make_vtable
518{
519 if (SCM_UNBNDP (printer))
520 printer = SCM_BOOL_F;
521
522 return scm_make_struct (scm_i_vtable_vtable_no_extra_fields, SCM_INUM0,
523 scm_list_2 (scm_make_struct_layout (fields),
524 printer));
525}
526#undef FUNC_NAME
527
528
d15ad007
LC
529/* Return true if S1 and S2 are equal structures, i.e., if their vtable and
530 contents are the same. Field protections are honored. Thus, it is an
531 error to test the equality of structures that contain opaque fields. */
532SCM
533scm_i_struct_equalp (SCM s1, SCM s2)
534#define FUNC_NAME "scm_i_struct_equalp"
535{
536 SCM vtable1, vtable2, layout;
537 size_t struct_size, field_num;
538
539 SCM_VALIDATE_STRUCT (1, s1);
540 SCM_VALIDATE_STRUCT (2, s2);
541
542 vtable1 = SCM_STRUCT_VTABLE (s1);
543 vtable2 = SCM_STRUCT_VTABLE (s2);
544
545 if (!scm_is_eq (vtable1, vtable2))
546 return SCM_BOOL_F;
547
548 layout = SCM_STRUCT_LAYOUT (s1);
549 struct_size = scm_i_symbol_length (layout) / 2;
550
551 for (field_num = 0; field_num < struct_size; field_num++)
552 {
553 SCM s_field_num;
554 SCM field1, field2;
555
556 /* We have to use `scm_struct_ref ()' here so that fields are accessed
557 consistently, notably wrt. field types and access rights. */
558 s_field_num = scm_from_size_t (field_num);
559 field1 = scm_struct_ref (s1, s_field_num);
560 field2 = scm_struct_ref (s2, s_field_num);
561
42ddb3cb
LC
562 /* Self-referencing fields (type `s') must be skipped to avoid infinite
563 recursion. */
564 if (!(scm_is_eq (field1, s1) && (scm_is_eq (field2, s2))))
565 if (scm_is_false (scm_equal_p (field1, field2)))
566 return SCM_BOOL_F;
d15ad007
LC
567 }
568
42ddb3cb
LC
569 /* FIXME: Tail elements should be tested for equality. */
570
d15ad007
LC
571 return SCM_BOOL_T;
572}
573#undef FUNC_NAME
574
575
0f2d19dd
JB
576\f
577
578
a1ec6916 579SCM_DEFINE (scm_struct_ref, "struct-ref", 2, 0, 0,
1bbd0b84 580 (SCM handle, SCM pos),
b6cf4d02 581 "Access the @var{n}th field of @var{struct}.\n\n"
b380b885
MD
582 "If the field is of type 'p', then it can be set to an arbitrary value.\n\n"
583 "If the field is of type 'u', then it can only be set to a non-negative\n"
584 "integer value small enough to fit in one machine word.")
1bbd0b84 585#define FUNC_NAME s_scm_struct_ref
0f2d19dd 586{
5e840c2e 587 SCM answer = SCM_UNDEFINED;
92c2555f 588 scm_t_bits * data;
0f2d19dd 589 SCM layout;
cc95e00a 590 size_t layout_len;
a55c2b68 591 size_t p;
92c2555f 592 scm_t_bits n_fields;
27646f41 593 scm_t_wchar field_type = 0;
0f2d19dd
JB
594
595
34d19ef6 596 SCM_VALIDATE_STRUCT (1, handle);
0f2d19dd
JB
597
598 layout = SCM_STRUCT_LAYOUT (handle);
599 data = SCM_STRUCT_DATA (handle);
a55c2b68 600 p = scm_to_size_t (pos);
0f2d19dd 601
cc95e00a 602 layout_len = scm_i_symbol_length (layout);
b6cf4d02
AW
603 n_fields = layout_len / 2;
604 if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1)))
605 n_fields += data[n_fields - 1];
2c36c351 606
34d19ef6 607 SCM_ASSERT_RANGE(1, pos, p < n_fields);
0f2d19dd 608
cc95e00a 609 if (p * 2 < layout_len)
2c36c351 610 {
27646f41
MG
611 scm_t_wchar ref;
612 field_type = scm_i_symbol_ref (layout, p * 2);
613 ref = scm_i_symbol_ref (layout, p * 2 + 1);
b6cf4d02 614 if ((ref != 'r') && (ref != 'w') && (ref != 'h'))
2c36c351
MD
615 {
616 if ((ref == 'R') || (ref == 'W'))
617 field_type = 'u';
618 else
1afff620 619 SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos));
2c36c351
MD
620 }
621 }
27646f41
MG
622 else if (scm_i_symbol_ref (layout, layout_len - 1) != 'O')
623 field_type = scm_i_symbol_ref(layout, layout_len - 2);
2c36c351 624 else
1afff620 625 SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos));
2c36c351 626
0f2d19dd
JB
627 switch (field_type)
628 {
629 case 'u':
b9bd8526 630 answer = scm_from_ulong (data[p]);
0f2d19dd
JB
631 break;
632
633#if 0
634 case 'i':
b9bd8526 635 answer = scm_from_long (data[p]);
0f2d19dd
JB
636 break;
637
638 case 'd':
f8de44c1 639 answer = scm_make_real (*((double *)&(data[p])));
0f2d19dd
JB
640 break;
641#endif
642
643 case 's':
644 case 'p':
d8c40b9f 645 answer = SCM_PACK (data[p]);
0f2d19dd
JB
646 break;
647
648
649 default:
2ade72d7 650 SCM_MISC_ERROR ("unrecognized field type: ~S",
1afff620 651 scm_list_1 (SCM_MAKE_CHAR (field_type)));
0f2d19dd
JB
652 }
653
654 return answer;
655}
1bbd0b84 656#undef FUNC_NAME
0f2d19dd
JB
657
658
a1ec6916 659SCM_DEFINE (scm_struct_set_x, "struct-set!", 3, 0, 0,
1bbd0b84 660 (SCM handle, SCM pos, SCM val),
e3239868
DH
661 "Set the slot of the structure @var{handle} with index @var{pos}\n"
662 "to @var{val}. Signal an error if the slot can not be written\n"
663 "to.")
1bbd0b84 664#define FUNC_NAME s_scm_struct_set_x
0f2d19dd 665{
92c2555f 666 scm_t_bits * data;
0f2d19dd 667 SCM layout;
cc95e00a 668 size_t layout_len;
a55c2b68 669 size_t p;
0f2d19dd 670 int n_fields;
27646f41 671 scm_t_wchar field_type = 0;
0f2d19dd 672
34d19ef6 673 SCM_VALIDATE_STRUCT (1, handle);
0f2d19dd
JB
674
675 layout = SCM_STRUCT_LAYOUT (handle);
676 data = SCM_STRUCT_DATA (handle);
a55c2b68 677 p = scm_to_size_t (pos);
0f2d19dd 678
cc95e00a 679 layout_len = scm_i_symbol_length (layout);
b6cf4d02
AW
680 n_fields = layout_len / 2;
681 if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1)))
682 n_fields += data[n_fields - 1];
0f2d19dd 683
34d19ef6 684 SCM_ASSERT_RANGE (1, pos, p < n_fields);
0f2d19dd 685
cc95e00a 686 if (p * 2 < layout_len)
2c36c351 687 {
e51fe79c 688 char set_x;
27646f41
MG
689 field_type = scm_i_symbol_ref (layout, p * 2);
690 set_x = scm_i_symbol_ref (layout, p * 2 + 1);
b6cf4d02 691 if (set_x != 'w' && set_x != 'h')
1afff620 692 SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos));
2c36c351 693 }
27646f41
MG
694 else if (scm_i_symbol_ref (layout, layout_len - 1) == 'W')
695 field_type = scm_i_symbol_ref (layout, layout_len - 2);
2c36c351 696 else
1afff620 697 SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos));
2c36c351 698
0f2d19dd
JB
699 switch (field_type)
700 {
701 case 'u':
d8c40b9f 702 data[p] = SCM_NUM2ULONG (3, val);
0f2d19dd
JB
703 break;
704
705#if 0
706 case 'i':
e4b265d8 707 data[p] = SCM_NUM2LONG (3, val);
0f2d19dd
JB
708 break;
709
710 case 'd':
711 *((double *)&(data[p])) = scm_num2dbl (val, (char *)SCM_ARG3);
712 break;
713#endif
714
715 case 'p':
d8c40b9f 716 data[p] = SCM_UNPACK (val);
0f2d19dd
JB
717 break;
718
719 case 's':
2ade72d7 720 SCM_MISC_ERROR ("self fields immutable", SCM_EOL);
0f2d19dd
JB
721
722 default:
2ade72d7 723 SCM_MISC_ERROR ("unrecognized field type: ~S",
1afff620 724 scm_list_1 (SCM_MAKE_CHAR (field_type)));
0f2d19dd
JB
725 }
726
727 return val;
728}
1bbd0b84 729#undef FUNC_NAME
0f2d19dd
JB
730
731
a1ec6916 732SCM_DEFINE (scm_struct_vtable, "struct-vtable", 1, 0, 0,
1bbd0b84 733 (SCM handle),
b380b885 734 "Return the vtable structure that describes the type of @var{struct}.")
1bbd0b84 735#define FUNC_NAME s_scm_struct_vtable
0f2d19dd 736{
34d19ef6 737 SCM_VALIDATE_STRUCT (1, handle);
0f2d19dd
JB
738 return SCM_STRUCT_VTABLE (handle);
739}
1bbd0b84 740#undef FUNC_NAME
0f2d19dd
JB
741
742
a1ec6916 743SCM_DEFINE (scm_struct_vtable_tag, "struct-vtable-tag", 1, 0, 0,
1bbd0b84 744 (SCM handle),
e3239868 745 "Return the vtable tag of the structure @var{handle}.")
1bbd0b84 746#define FUNC_NAME s_scm_struct_vtable_tag
0f2d19dd 747{
34d19ef6 748 SCM_VALIDATE_VTABLE (1, handle);
b9bd8526 749 return scm_from_ulong (((unsigned long)SCM_STRUCT_DATA (handle)) >> 3);
98d5f601 750}
1bbd0b84 751#undef FUNC_NAME
98d5f601
MD
752
753/* {Associating names and classes with vtables}
754 *
755 * The name of a vtable should probably be stored as a slot. This is
756 * a backward compatible solution until agreement has been achieved on
757 * how to associate names with vtables.
758 */
759
c014a02e 760unsigned long
d587c9e8 761scm_struct_ihashq (SCM obj, unsigned long n, void *closure)
98d5f601 762{
ad196599
MD
763 /* The length of the hash table should be a relative prime it's not
764 necessary to shift down the address. */
f1267706 765 return SCM_UNPACK (obj) % n;
98d5f601
MD
766}
767
768SCM
769scm_struct_create_handle (SCM obj)
770{
771 SCM handle = scm_hash_fn_create_handle_x (scm_struct_table,
772 obj,
773 SCM_BOOL_F,
774 scm_struct_ihashq,
d587c9e8 775 (scm_t_assoc_fn) scm_sloppy_assq,
98d5f601 776 0);
7888309b 777 if (scm_is_false (SCM_CDR (handle)))
98d5f601
MD
778 SCM_SETCDR (handle, scm_cons (SCM_BOOL_F, SCM_BOOL_F));
779 return handle;
780}
781
a1ec6916 782SCM_DEFINE (scm_struct_vtable_name, "struct-vtable-name", 1, 0, 0,
1bbd0b84 783 (SCM vtable),
e3239868 784 "Return the name of the vtable @var{vtable}.")
1bbd0b84 785#define FUNC_NAME s_scm_struct_vtable_name
98d5f601 786{
34d19ef6 787 SCM_VALIDATE_VTABLE (1, vtable);
98d5f601
MD
788 return SCM_STRUCT_TABLE_NAME (SCM_CDR (scm_struct_create_handle (vtable)));
789}
1bbd0b84 790#undef FUNC_NAME
98d5f601 791
a1ec6916 792SCM_DEFINE (scm_set_struct_vtable_name_x, "set-struct-vtable-name!", 2, 0, 0,
1bbd0b84 793 (SCM vtable, SCM name),
e3239868 794 "Set the name of the vtable @var{vtable} to @var{name}.")
1bbd0b84 795#define FUNC_NAME s_scm_set_struct_vtable_name_x
98d5f601 796{
34d19ef6
HWN
797 SCM_VALIDATE_VTABLE (1, vtable);
798 SCM_VALIDATE_SYMBOL (2, name);
98d5f601
MD
799 SCM_SET_STRUCT_TABLE_NAME (SCM_CDR (scm_struct_create_handle (vtable)),
800 name);
801 return SCM_UNSPECIFIED;
0f2d19dd 802}
1bbd0b84 803#undef FUNC_NAME
0f2d19dd
JB
804
805
806\f
807
bafcafb2 808void
1bbd0b84 809scm_print_struct (SCM exp, SCM port, scm_print_state *pstate)
bafcafb2 810{
7888309b 811 if (scm_is_true (scm_procedure_p (SCM_STRUCT_PRINTER (exp))))
4bfdf158
MD
812 scm_printer_apply (SCM_STRUCT_PRINTER (exp), exp, port, pstate);
813 else
bafcafb2 814 {
a1ae1799
MD
815 SCM vtable = SCM_STRUCT_VTABLE (exp);
816 SCM name = scm_struct_vtable_name (vtable);
817 scm_puts ("#<", port);
7888309b 818 if (scm_is_true (name))
b6cf4d02
AW
819 {
820 scm_display (name, port);
821 scm_putc (' ', port);
822 }
a1ae1799 823 else
b6cf4d02
AW
824 {
825 if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_VTABLE))
826 scm_puts ("vtable:", port);
827 else
828 scm_puts ("struct:", port);
829 scm_uintprint (SCM_UNPACK (vtable), 16, port);
830 scm_putc (' ', port);
831 scm_write (SCM_VTABLE_LAYOUT (vtable), port);
832 scm_putc (' ', port);
833 }
0345e278 834 scm_uintprint (SCM_UNPACK (exp), 16, port);
b6cf4d02
AW
835 /* hackety hack */
836 if (SCM_STRUCT_APPLICABLE_P (exp))
837 {
838 if (scm_is_true (SCM_STRUCT_PROCEDURE (exp)))
839 {
840 scm_puts (" proc: ", port);
841 if (scm_is_true (scm_procedure_p (SCM_STRUCT_PROCEDURE (exp))))
842 scm_write (SCM_STRUCT_PROCEDURE (exp), port);
843 else
844 scm_puts ("(not a procedure?)", port);
845 }
846 if (SCM_STRUCT_SETTER_P (exp))
847 {
848 scm_puts (" setter: ", port);
849 scm_write (SCM_STRUCT_SETTER (exp), port);
850 }
851 }
b7f3516f 852 scm_putc ('>', port);
bafcafb2 853 }
bafcafb2 854}
1cc91f1b 855
0f2d19dd
JB
856void
857scm_init_struct ()
0f2d19dd 858{
b6cf4d02
AW
859 SCM scm_applicable_struct_vtable_vtable;
860 SCM scm_applicable_struct_with_setter_vtable_vtable;
861
862 GC_REGISTER_DISPLACEMENT (2*sizeof(scm_t_bits)); /* for the self data pointer */
863 GC_REGISTER_DISPLACEMENT (2*sizeof(scm_t_bits)
864 + scm_tc3_struct); /* for the vtable data pointer */
865
866 scm_struct_table = scm_make_weak_key_hash_table (scm_from_int (31));
867 required_vtable_fields = scm_from_locale_string (SCM_VTABLE_BASE_LAYOUT);
868 required_applicable_fields = scm_from_locale_string (SCM_APPLICABLE_BASE_LAYOUT);
869 required_applicable_with_setter_fields = scm_from_locale_string (SCM_APPLICABLE_WITH_SETTER_BASE_LAYOUT);
651f2cd2
KR
870
871 scm_i_vtable_vtable_no_extra_fields =
b6cf4d02
AW
872 scm_make_vtable_vtable (scm_nullstr, SCM_INUM0, SCM_EOL);
873
874 scm_applicable_struct_vtable_vtable =
875 scm_make_struct (scm_i_vtable_vtable_no_extra_fields, SCM_INUM0,
876 scm_list_1 (scm_make_struct_layout (required_vtable_fields)));
877 SCM_SET_VTABLE_FLAGS (scm_applicable_struct_vtable_vtable,
878 SCM_VTABLE_FLAG_APPLICABLE_VTABLE);
879 scm_c_define ("<applicable-struct-vtable>", scm_applicable_struct_vtable_vtable);
880
881 scm_applicable_struct_with_setter_vtable_vtable =
882 scm_make_struct (scm_i_vtable_vtable_no_extra_fields, SCM_INUM0,
883 scm_list_1 (scm_make_struct_layout (required_vtable_fields)));
884 SCM_SET_VTABLE_FLAGS (scm_applicable_struct_with_setter_vtable_vtable,
885 SCM_VTABLE_FLAG_APPLICABLE_VTABLE | SCM_VTABLE_FLAG_SETTER_VTABLE);
886 scm_c_define ("<applicable-struct-with-setter-vtable>", scm_applicable_struct_with_setter_vtable_vtable);
651f2cd2 887
e11e83f3 888 scm_c_define ("vtable-index-layout", scm_from_int (scm_vtable_index_layout));
86d31dfe 889 scm_c_define ("vtable-index-printer",
b6cf4d02 890 scm_from_int (scm_vtable_index_instance_printer));
e11e83f3 891 scm_c_define ("vtable-offset-user", scm_from_int (scm_vtable_offset_user));
a0599745 892#include "libguile/struct.x"
0f2d19dd 893}
89e00824
ML
894
895/*
896 Local Variables:
897 c-file-style: "gnu"
898 End:
899*/