1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36: #define LIBGAUCHE_BODY
37: #include "gauche.h"
38: #include "gauche/port.h"
39: #include "gauche/builtin-syms.h"
40:
41: #include <ctype.h>
42:
43: static void write_walk(ScmObj obj, ScmPort *port, ScmWriteContext *ctx);
44: static void write_ss(ScmObj obj, ScmPort *port, ScmWriteContext *ctx);
45: static void write_ss_rec(ScmObj obj, ScmPort *port, ScmWriteContext *ctx);
46: static void write_object(ScmObj obj, ScmPort *out, ScmWriteContext *ctx);
47: static ScmObj write_object_fallback(ScmObj *args, int nargs, ScmGeneric *gf);
48: static void format_write(ScmObj obj, ScmPort *port, ScmWriteContext *ctx,
49: int sharedp);
50: SCM_DEFINE_GENERIC(Scm_GenericWriteObject, write_object_fallback, NULL);
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105: #define SPBUFSIZ 50
106:
107:
108: #define WRITE_LIMITED 0x10
109: #define WRITE_CIRCULAR 0x20
110:
111:
112:
113:
114: #define DEFAULT_CASE \
115: (SCM_VM_RUNTIME_FLAG_IS_SET(Scm_VM(), SCM_CASE_FOLD)? \
116: SCM_WRITE_CASE_FOLD:SCM_WRITE_CASE_NOFOLD)
117:
118:
119:
120:
121: void Scm_Write(ScmObj obj, ScmObj p, int mode)
122: {
123: ScmWriteContext ctx;
124: ScmVM *vm;
125: ScmPort *port;
126:
127: if (!SCM_OPORTP(p)) {
128: Scm_Error("output port required, but got %S", p);
129: }
130: port = SCM_PORT(p);
131: ctx.mode = mode;
132: ctx.flags = 0;
133:
134:
135: if (port->flags & SCM_PORT_WALKING) {
136: SCM_ASSERT(SCM_PAIRP(port->data)&&SCM_HASH_TABLE_P(SCM_CDR(port->data)));
137: write_walk(obj, port, &ctx);
138: return;
139: }
140:
141: if (port->flags & SCM_PORT_WRITESS) {
142: SCM_ASSERT(SCM_PAIRP(port->data)&&SCM_HASH_TABLE_P(SCM_CDR(port->data)));
143: write_ss_rec(obj, port, &ctx);
144: return;
145: }
146:
147:
148: if (SCM_WRITE_CASE(&ctx) == 0) ctx.mode |= DEFAULT_CASE;
149:
150: vm = Scm_VM();
151: PORT_LOCK(port, vm);
152: if (SCM_WRITE_MODE(&ctx) == SCM_WRITE_SHARED) {
153: PORT_SAFE_CALL(port, write_ss(obj, port, &ctx));
154: } else {
155: PORT_SAFE_CALL(port, write_ss_rec(obj, port, &ctx));
156: }
157: PORT_UNLOCK(port);
158: }
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170: int Scm_WriteLimited(ScmObj obj, ScmObj port, int mode, int width)
171: {
172: ScmWriteContext ctx;
173: ScmString *str;
174: ScmObj out;
175: int nc, sharedp = FALSE;
176:
177: if (!SCM_OPORTP(port))
178: Scm_Error("output port required, but got %S", port);
179: out = Scm_MakeOutputStringPort(TRUE);
180: SCM_PORT(out)->data = SCM_PORT(port)->data;
181: ctx.mode = mode;
182: ctx.flags = WRITE_LIMITED;
183: ctx.limit = width;
184:
185: if (SCM_WRITE_CASE(&ctx) == 0) ctx.mode |= DEFAULT_CASE;
186:
187: if (SCM_PORT(port)->flags & SCM_PORT_WALKING) {
188: SCM_ASSERT(SCM_PAIRP(SCM_PORT(port)->data)&&SCM_HASH_TABLE_P(SCM_CDR(SCM_PORT(port)->data)));
189: write_walk(obj, SCM_PORT(port), &ctx);
190: return 0;
191: }
192:
193: sharedp = SCM_WRITE_MODE(&ctx) == SCM_WRITE_SHARED;
194: format_write(obj, SCM_PORT(out), &ctx, sharedp);
195: str = SCM_STRING(Scm_GetOutputString(SCM_PORT(out), 0));
196: nc = SCM_STRING_BODY_LENGTH(SCM_STRING_BODY(str));
197: if (nc > width) {
198: ScmObj sub = Scm_Substring(str, 0, width, FALSE);
199: SCM_PUTS(sub, port);
200: return -1;
201: } else {
202: SCM_PUTS(str, port);
203: return nc;
204: }
205: }
206:
207:
208:
209:
210:
211: int Scm_WriteCircular(ScmObj obj, ScmObj port, int mode, int width)
212: {
213: ScmObj out;
214: ScmString *str;
215: ScmWriteContext ctx;
216: int nc;
217:
218: if (!SCM_OPORTP(port)) {
219: Scm_Error("output port required, but got %S", port);
220: }
221: ctx.mode = mode;
222: ctx.flags = WRITE_CIRCULAR;
223: if (SCM_WRITE_CASE(&ctx) == 0) ctx.mode |= DEFAULT_CASE;
224: if (width > 0) {
225: ctx.flags |= WRITE_LIMITED;
226: ctx.limit = width;
227: }
228: ctx.ncirc = 0;
229: ctx.table = SCM_HASH_TABLE(Scm_MakeHashTableSimple(SCM_HASH_EQ, 8));
230:
231: if (width <= 0) {
232: ScmVM *vm = Scm_VM();
233: PORT_LOCK(SCM_PORT(port), vm);
234: PORT_SAFE_CALL(SCM_PORT(port),
235: format_write(obj, SCM_PORT(port), &ctx, TRUE));
236: PORT_UNLOCK(SCM_PORT(port));
237: return 0;
238: }
239:
240: if (SCM_PORT(port)->flags & SCM_PORT_WALKING) {
241: SCM_ASSERT(SCM_PAIRP(SCM_PORT(port)->data)&&SCM_HASH_TABLE_P(SCM_CDR(SCM_PORT(port)->data)));
242: write_walk(obj, SCM_PORT(port), &ctx);
243: return 0;
244: }
245:
246: out = Scm_MakeOutputStringPort(TRUE);
247: SCM_PORT(out)->data = SCM_PORT(port)->data;
248:
249: format_write(obj, SCM_PORT(out), &ctx, TRUE);
250: str = SCM_STRING(Scm_GetOutputString(SCM_PORT(out),0));
251: nc = SCM_STRING_BODY_LENGTH(SCM_STRING_BODY(str));
252: if (nc > width) {
253: ScmObj sub = Scm_Substring(str, 0, width, FALSE);
254: SCM_PUTS(sub, port);
255: return -1;
256: } else {
257: SCM_PUTS(str, port);
258: return nc;
259: }
260: }
261:
262:
263:
264:
265:
266:
267: static const char *char_names[] = {
268: "null", "x01", "x02", "x03", "x04", "x05", "x06", "x07",
269: "x08", "tab", "newline","x0b", "x0c", "return","x0e", "x0f",
270: "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17",
271: "x18", "x19", "x1a", "escape","x1c", "x1d", "x1e", "x1f",
272: "space"
273: };
274:
275: #define CASE_ITAG(obj, str) \
276: case SCM_ITAG(obj): Scm_PutzUnsafe(str, -1, port); break;
277:
278:
279: static void write_general(ScmObj obj, ScmPort *out, ScmWriteContext *ctx)
280: {
281: ScmClass *c = Scm_ClassOf(obj);
282: if (c->print) c->print(obj, out, ctx);
283: else write_object(obj, out, ctx);
284: }
285:
286:
287:
288:
289:
290: static void write_object(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
291: {
292: Scm_ApplyRec(SCM_OBJ(&Scm_GenericWriteObject),
293: SCM_LIST2(obj, SCM_OBJ(port)));
294: }
295:
296:
297: static ScmObj write_object_fallback(ScmObj *args, int nargs, ScmGeneric *gf)
298: {
299: ScmClass *klass;
300: if (nargs != 2 || (nargs == 2 && !SCM_OPORTP(args[1]))) {
301: Scm_Error("No applicable method for write-object with %S",
302: Scm_ArrayToList(args, nargs));
303: }
304: klass = Scm_ClassOf(args[0]);
305: Scm_Printf(SCM_PORT(args[1]), "#<%A%s%p>",
306: klass->name,
307: (SCM_FALSEP(klass->redefined)? " " : ":redefined "),
308: args[0]);
309: return SCM_TRUE;
310: }
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332: static ScmPortVTable walker_port_vtable = {
333: NULL, NULL, NULL, NULL, NULL,
334: NULL, NULL, NULL, NULL, NULL,
335: NULL, NULL
336: };
337:
338: static ScmPort *make_walker_port(void)
339: {
340: ScmPort *port;
341: ScmObj ht;
342:
343: port = SCM_PORT(Scm_MakeVirtualPort(SCM_CLASS_PORT, SCM_PORT_OUTPUT,
344: &walker_port_vtable));
345: ht = Scm_MakeHashTableSimple(SCM_HASH_EQ, 0);
346: port->data = Scm_Cons(SCM_MAKE_INT(0), ht);
347: port->flags = SCM_PORT_WALKING;
348: return port;
349: }
350:
351:
352: static void write_walk(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
353: {
354: ScmHashEntry *e;
355: ScmHashTable *ht;
356: ScmObj elt;
357:
358: ht = SCM_HASH_TABLE(SCM_CDR(port->data));
359:
360: for (;;) {
361: if (!SCM_PTRP(obj) || SCM_SYMBOLP(obj) || SCM_KEYWORDP(obj)
362: || SCM_NUMBERP(obj)) {
363: return;
364: }
365:
366: if (SCM_PAIRP(obj)) {
367: e = Scm_HashTableGet(ht, obj);
368: if (e) { e->value = SCM_TRUE; return; }
369: Scm_HashTablePut(ht, obj, SCM_FALSE);
370:
371: elt = SCM_CAR(obj);
372: if (SCM_PTRP(elt)) write_walk(SCM_CAR(obj), port, ctx);
373: obj = SCM_CDR(obj);
374: continue;
375: }
376: if (SCM_STRINGP(obj) && !SCM_STRING_NULL_P(obj)) {
377: e = Scm_HashTableGet(ht, obj);
378: if (e) { e->value = SCM_TRUE; return; }
379: Scm_HashTablePut(ht, obj, SCM_FALSE);
380: return;
381: }
382: if (SCM_VECTORP(obj) && SCM_VECTOR_SIZE(obj) > 0) {
383: int i, len = SCM_VECTOR_SIZE(obj);
384:
385: e = Scm_HashTableGet(ht, obj);
386: if (e) { e->value = SCM_TRUE; return; }
387: Scm_HashTablePut(ht, obj, SCM_FALSE);
388:
389: for (i=0; i<len; i++) {
390: elt = SCM_VECTOR_ELEMENT(obj, i);
391: if (SCM_PTRP(elt)) write_walk(elt, port, ctx);
392: }
393: return;
394: }
395: else {
396:
397:
398: e = Scm_HashTableGet(ht, obj);
399: if (e) { e->value = SCM_TRUE; return; }
400: Scm_HashTablePut(ht, obj, SCM_FALSE);
401:
402: write_general(obj, port, ctx);
403: return;
404: }
405: }
406: }
407:
408:
409: static void write_ss_rec(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
410: {
411: ScmHashEntry *e;
412: char numbuf[50];
413: ScmHashTable *ht = NULL;
414:
415: if (ctx->flags & WRITE_LIMITED) {
416: if (port->src.ostr.length >= ctx->limit) return;
417: }
418:
419: if (SCM_PAIRP(port->data) && SCM_HASH_TABLE_P(SCM_CDR(port->data))) {
420: ht = SCM_HASH_TABLE(SCM_CDR(port->data));
421: }
422:
423: if (!SCM_PTRP(obj)) {
424: if (SCM_IMMEDIATEP(obj)) {
425: switch (SCM_ITAG(obj)) {
426: CASE_ITAG(SCM_FALSE, "#f");
427: CASE_ITAG(SCM_TRUE, "#t");
428: CASE_ITAG(SCM_NIL, "()");
429: CASE_ITAG(SCM_EOF, "#<eof>");
430: CASE_ITAG(SCM_UNDEFINED, "#<undef>");
431: CASE_ITAG(SCM_UNBOUND, "#<unbound>");
432: default:
433: Scm_Panic("write: unknown itag object: %08x", SCM_WORD(obj));
434: }
435: }
436: else if (SCM_INTP(obj)) {
437: char buf[SPBUFSIZ];
438: snprintf(buf, SPBUFSIZ, "%ld", SCM_INT_VALUE(obj));
439: Scm_PutzUnsafe(buf, -1, port);
440: }
441: else if (SCM_CHARP(obj)) {
442: ScmChar ch = SCM_CHAR_VALUE(obj);
443: if (SCM_WRITE_MODE(ctx) == SCM_WRITE_DISPLAY) {
444: Scm_PutcUnsafe(ch, port);
445: } else {
446: Scm_PutzUnsafe("#\\", -1, port);
447: if (ch <= 0x20) Scm_PutzUnsafe(char_names[ch], -1, port);
448: else if (ch == 0x7f) Scm_PutzUnsafe("del", -1, port);
449: else Scm_PutcUnsafe(ch, port);
450: }
451: }
452: else Scm_Panic("write: got a bogus object: %08x", SCM_WORD(obj));
453: return;
454: }
455: if (SCM_NUMBERP(obj)) {
456:
457: write_general(obj, port, ctx);
458: return;
459: }
460:
461: if ((SCM_STRINGP(obj) && SCM_STRING_NULL_P(obj))
462: || (SCM_VECTORP(obj) && SCM_VECTOR_SIZE(obj) == 0)) {
463:
464: write_general(obj, port, ctx);
465: return;
466: }
467:
468: if (ht) {
469: e = Scm_HashTableGet(ht, obj);
470: if (e && e->value != SCM_FALSE) {
471: if (SCM_INTP(e->value)) {
472:
473: snprintf(numbuf, 50, "#%ld#", SCM_INT_VALUE(e->value));
474: Scm_PutzUnsafe(numbuf, -1, port);
475: return;
476: } else {
477:
478: int count = SCM_INT_VALUE(SCM_CAR(port->data));
479: snprintf(numbuf, 50, "#%d=", count);
480: e->value = SCM_MAKE_INT(count);
481: SCM_SET_CAR(port->data, SCM_MAKE_INT(count+1));
482: Scm_PutzUnsafe(numbuf, -1, port);
483: }
484: }
485: }
486:
487:
488: if (SCM_PAIRP(obj)) {
489:
490: if (SCM_PAIRP(SCM_CDR(obj)) && SCM_NULLP(SCM_CDDR(obj))) {
491: int special = TRUE;
492: if (SCM_CAR(obj) == SCM_SYM_QUOTE) {
493: Scm_PutcUnsafe('\'', port);
494: } else if (SCM_CAR(obj) == SCM_SYM_QUASIQUOTE) {
495: Scm_PutcUnsafe('`', port);
496: } else if (SCM_CAR(obj) == SCM_SYM_UNQUOTE) {
497: Scm_PutcUnsafe(',', port);
498: } else if (SCM_CAR(obj) == SCM_SYM_UNQUOTE_SPLICING) {
499: Scm_PutzUnsafe(",@", -1, port);
500: } else {
501: special = FALSE;
502: }
503: if (special) {
504: write_ss_rec(SCM_CADR(obj), port, ctx);
505: return;
506: }
507: }
508:
509:
510: Scm_PutcUnsafe('(', port);
511: for (;;) {
512:
513: write_ss_rec(SCM_CAR(obj), port, ctx);
514:
515: obj = SCM_CDR(obj);
516: if (SCM_NULLP(obj)) { Scm_PutcUnsafe(')', port); return; }
517: if (!SCM_PAIRP(obj)) {
518: Scm_PutzUnsafe(" . ", -1, port);
519: write_ss_rec(obj, port, ctx);
520: Scm_PutcUnsafe(')', port);
521: return;
522: }
523: if (ht) {
524: e = Scm_HashTableGet(ht, obj);
525: if (e && e->value != SCM_FALSE) {
526: Scm_PutzUnsafe(" . ", -1, port);
527: write_ss_rec(obj, port, ctx);
528: Scm_PutcUnsafe(')', port);
529: return;
530: }
531: }
532: Scm_PutcUnsafe(' ', port);
533: }
534: } else if (SCM_VECTORP(obj)) {
535: int len, i;
536: ScmObj *elts;
537:
538: Scm_PutzUnsafe("#(", -1, port);
539: len = SCM_VECTOR_SIZE(obj);
540: elts = SCM_VECTOR_ELEMENTS(obj);
541: for (i=0; i<len-1; i++) {
542: write_ss_rec(elts[i], port, ctx);
543: Scm_PutcUnsafe(' ', port);
544: }
545: write_ss_rec(elts[i], port, ctx);
546: Scm_PutcUnsafe(')', port);
547: } else {
548:
549: write_general(obj, port, ctx);
550: }
551: }
552: