1 /* JitterLisp: printer.
2 
3    Copyright (C) 2017, 2018, 2019, 2020 Luca Saiu
4    Written by Luca Saiu
5 
6    This file is part of the JitterLisp language implementation, distributed as
7    an example along with Jitter under the same license.
8 
9    Jitter is free software: you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13 
14    Jitter is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with Jitter.  If not, see <http://www.gnu.org/licenses/>. */
21 
22 
23 /* Include the Gnulib header. */
24 #include <config.h>
25 
26 #include "jitterlisp-printer.h"
27 
28 #include <stdio.h>
29 #include <unistd.h>
30 
31 #include <jitter/jitter-dynamic-buffer.h>
32 #include <jitter/jitter-fatal.h>
33 #include <jitter/jitter-print.h>
34 
35 #include "jitterlisp-settings.h"
36 #include "jitterlisp-sexpression.h"
37 #include "jitterlisp-ast.h"
38 
39 
40 
41 
42 /* Sharing-recognition hashing.
43  * ************************************************************************** */
44 
45 /* This hash table data structure serves to avoid infinite loops when printing
46    circular structures, and to show which substructures are shared (currently
47    without showing *what* is shared, but this can be improved).
48    Since this data structure and the printing process in general doesn't use
49    Lisp heap allocation in this case it's safe to simply hash on tagged
50    objects, even if they are pointers, even with a moving GC -- which is not
51    there yet but should come at some point. */
52 
53 /* Initialize a sharing table, already allocated by the user.  This is
54    intended to be used on a pointer to an automatic variable. */
55 static void
jitterlisp_sharing_table_initialize(struct jitter_hash_table * t)56 jitterlisp_sharing_table_initialize (struct jitter_hash_table *t)
57 {
58   jitter_hash_initialize (t);
59 }
60 
61 /* Finalize a sharing table, already allocated by the user.  This is
62    intended to be used on a pointer to an automatic variable. */
63 static void
jitterlisp_sharing_table_finalize(struct jitter_hash_table * t)64 jitterlisp_sharing_table_finalize (struct jitter_hash_table *t)
65 {
66   jitter_word_hash_finalize (t, jitter_do_nothing_on_word);
67 }
68 
69 /* Return non-false iff the given object is already in the sharing table. */
70 static bool
jitterlisp_sharing_table_has(struct jitter_hash_table * t,jitterlisp_object o)71 jitterlisp_sharing_table_has (struct jitter_hash_table *t,
72                               jitterlisp_object o)
73 {
74   return jitter_word_hash_table_has (t, o);
75 }
76 
77 /* Add the given object to the sharing table, if the object can potentially
78    be circular.  Do nothing otherwise.  This doesn't check whether the object is
79    already in the table: it would be a useless source of inefficiency.  */
80 static void
jitterlisp_sharing_table_add(struct jitter_hash_table * t,jitterlisp_object o)81 jitterlisp_sharing_table_add (struct jitter_hash_table *t,
82                               jitterlisp_object o)
83 {
84   /* A Lisp object whose printed representation cannot contain other Lisp
85      object should not be kept in the table; do nothing in that case. */
86   if (! JITTERLISP_IS_RECURSIVE(o))
87     return;
88 
89   /* Add an entry to the table.  The value is not used. */
90   union jitter_word useless = { .fixnum = 0 };
91   jitter_word_hash_table_add (t, o, useless);
92 }
93 
94 
95 
96 
97 /* Character names.
98  * ************************************************************************** */
99 
100 const struct jitterlisp_character_name_binding
101 jitterlisp_non_ordinary_character_name_bindings []
102   =
103     {
104       { '\0', "nul" },
105       { ' ',  "space" },
106       { '\n', "newline" },
107       { '\n', "linefeed" },
108       { '\r', "cr" },
109       { '\r', "return" },
110       { '\f', "page" }
111     };
112 
113 const size_t
114 jitterlisp_non_ordinary_character_name_binding_no
115   = (sizeof (jitterlisp_non_ordinary_character_name_bindings)
116      / sizeof (const struct jitterlisp_character_name_binding));
117 
118 
119 
120 
121 /* Char-printing utility.
122  * ************************************************************************** */
123 
124 /* Use the given char-printer to emit a printed representation of the given
125    character, be it ordinary or non-ordinary. */
126 static void
jitterlisp_print_character_name(jitter_print_context cx,jitter_int c)127 jitterlisp_print_character_name (jitter_print_context cx, jitter_int c)
128 {
129   /* Print the #\ prefix, which is the same for ordinary and non-ordinary
130      characters. */
131   jitter_print_char_star (cx, "#\\");
132 
133   /* Look for the first name binding for c as a non-ordinary character.  If one
134      exists, print it and return. */
135   int i;
136   for (i = 0; i < jitterlisp_non_ordinary_character_name_binding_no; i ++)
137     if (jitterlisp_non_ordinary_character_name_bindings [i].character == c)
138       {
139         char *name = jitterlisp_non_ordinary_character_name_bindings [i].name;
140         jitter_print_char_star (cx, name);
141         return;
142       }
143 
144   /* Since we haven't found a binding c must be an ordinary character.  Print it
145      as it is. */
146   jitter_print_char (cx, c);
147 }
148 
149 /* Begin the named class in the given print context, unless colorising has
150    been disabled. */
151 void
jitterlisp_begin_class(jitter_print_context cx,const char * name_suffix)152 jitterlisp_begin_class (jitter_print_context cx, const char *name_suffix)
153 {
154   char buffer [1000];
155   sprintf (buffer, "jitterlisp-%s", name_suffix);
156   jitter_print_begin_class (cx, buffer);
157 }
158 
159 /* End the last begun class in the given print context, unless colorising has
160    been disabled. */
161 void
jitterlisp_end_class(jitter_print_context cx)162 jitterlisp_end_class (jitter_print_context cx)
163 {
164   jitter_print_end_class (cx);
165 }
166 
167 
168 
169 
170 /* S-expression printer.
171  * ************************************************************************** */
172 
173 /* Forward declaration.  Print the given object in the given print context using
174    the pointed sharing table. */
175 static void
176 jitterlisp_print_recursive (jitter_print_context cx,
177                             struct jitter_hash_table *st,
178                             jitterlisp_object o);
179 
180 
181 /* Print o as the cdr of a cons, with the car already printed and the
182    surrounding parentheses printed by the caller, using the given
183    char-printer. */
184 static void
jitterlisp_print_cdr(jitter_print_context cx,struct jitter_hash_table * st,jitterlisp_object o)185 jitterlisp_print_cdr (jitter_print_context cx,
186                       struct jitter_hash_table *st, jitterlisp_object o)
187 {
188   /* Show sharing. */
189   if (jitterlisp_sharing_table_has (st, o))
190     {
191       jitterlisp_begin_class (cx, "cons");
192       jitter_print_char_star (cx, " . ");
193       jitterlisp_end_class (cx);
194       jitterlisp_begin_class (cx, "circular");
195       jitter_print_char_star (cx, "...");
196       jitterlisp_end_class (cx);
197       return;
198     }
199   jitterlisp_sharing_table_add (st, o);
200 
201   if (JITTERLISP_IS_EMPTY_LIST(o))
202     {
203       /* There is nothing to print, not even a space: the caller has already
204          written the open parens and will append the matching closed parens
205          right after this function returns. */
206     }
207   else if (JITTERLISP_IS_CONS(o))
208     {
209       /* So, o is another cons: print o's car as the next list element, but
210          first separate it from the previous element, which must exist if we got
211          here, with a space. */
212       jitterlisp_begin_class (cx, "cons");
213       jitter_print_char (cx, ' ');
214       jitterlisp_end_class (cx);
215       struct jitterlisp_cons * const c = JITTERLISP_CONS_DECODE(o);
216       jitterlisp_print_recursive (cx, st, c->car);
217 
218       /* We're still within a list or improper/dotted list and so we'll keep
219          using cdr notation for o's cdr, without adding more parens.  If o's cdr
220          is still a cons then the recursive call will prepend a space to the
221          elements. */
222       jitterlisp_print_cdr (cx, st, c->cdr);
223     }
224   else
225     {
226       /* The innermost cdr of the spine is not (): this is an improper/dotted
227          list. */
228       jitterlisp_begin_class (cx, "cons");
229       jitter_print_char_star (cx, " . ");
230       jitterlisp_end_class (cx);
231       jitterlisp_print_recursive (cx, st, o);
232     }
233 }
234 
235 /* Print the pointed s-expressions in order starting from the given initial
236    pointer and going on for element_no elements.  Use a single space as a
237    separator before each element, including the first. */
238 static void
jitterlisp_print_subs(jitter_print_context cx,struct jitter_hash_table * st,jitterlisp_object * elements,size_t element_no)239 jitterlisp_print_subs (jitter_print_context cx,
240                        struct jitter_hash_table *st,
241                        jitterlisp_object *elements, size_t element_no)
242 {
243   int i;
244   for (i = 0; i < element_no; i ++)
245     {
246       jitterlisp_begin_class (cx, "ast");
247       jitter_print_char (cx, ' ');
248       jitterlisp_end_class (cx);
249       jitterlisp_print_recursive (cx, st, elements [i]);
250     }
251 }
252 
253 /* Print the pointed AST to the given context using the pointed share table. */
254 static void
jitterlisp_print_ast(jitter_print_context cx,struct jitter_hash_table * st,struct jitterlisp_ast * ast)255 jitterlisp_print_ast (jitter_print_context cx,
256                       struct jitter_hash_table *st, struct jitterlisp_ast *ast)
257 {
258   /* There's no need to check for sharing here: this function is only called
259      by jitterlisp_print_recursive which has already done it on the same
260      argument, and the AST subs are printed thru jitterlisp_print_recursive . */
261 
262   jitterlisp_begin_class (cx, "ast");
263   jitter_print_char_star (cx, "[");
264   switch (ast->case_)
265     {
266     case jitterlisp_ast_case_literal:
267       jitter_print_char_star (cx, "literal");
268       break;
269     case jitterlisp_ast_case_variable:
270       jitter_print_char_star (cx, "variable");
271       break;
272     case jitterlisp_ast_case_define:
273       jitter_print_char_star (cx, "define");
274       break;
275     case jitterlisp_ast_case_if:
276       jitter_print_char_star (cx, "if");
277       break;
278     case jitterlisp_ast_case_setb:
279       jitter_print_char_star (cx, "set!");
280       break;
281     case jitterlisp_ast_case_while:
282       jitter_print_char_star (cx, "while");
283       break;
284     case jitterlisp_ast_case_primitive:
285       jitter_print_char_star (cx, "primitive "); /* Space.  See below. */
286       break;
287     case jitterlisp_ast_case_call:
288       jitter_print_char_star (cx, "call");
289       break;
290     case jitterlisp_ast_case_lambda:
291       jitter_print_char_star (cx, "lambda");
292       break;
293     case jitterlisp_ast_case_let:
294       jitter_print_char_star (cx, "let");
295       break;
296     case jitterlisp_ast_case_sequence:
297       jitter_print_char_star (cx, "sequence");
298       break;
299     default:
300       jitter_print_char_star (cx, "invalid]");
301       jitterlisp_end_class (cx);
302       return;
303     }
304   jitterlisp_end_class (cx);
305   /* I can have a special case for primitives: instead of printing the entire
306      primitive object, which is very verbose, just print the primitive name when
307      occurring within a primitive AST.  There is no ambiguity, and the notation
308      gets much leaner: */
309   if (ast->case_ == jitterlisp_ast_case_primitive)
310     {
311       struct jitterlisp_primitive * const primitive
312         = JITTERLISP_PRIMITIVE_DECODE(ast->subs [0]);
313       jitterlisp_begin_class (cx, "primitive");
314       jitter_print_char_star (cx, primitive->name);
315       jitterlisp_end_class (cx);
316       jitterlisp_print_subs (cx, st, ast->subs + 1, ast->sub_no - 1);
317     }
318   else
319     /* Default non-primitive case: print every sub using its own decoration. */
320     jitterlisp_print_subs (cx, st, ast->subs, ast->sub_no);
321   jitterlisp_begin_class (cx, "ast");
322   jitter_print_char_star (cx, "]");
323   jitterlisp_end_class (cx);
324 }
325 
326 static void
jitterlisp_print_recursive(jitter_print_context cx,struct jitter_hash_table * st,jitterlisp_object o)327 jitterlisp_print_recursive (jitter_print_context cx,
328                             struct jitter_hash_table *st, jitterlisp_object o)
329 {
330   /* Before printing anything, check whether we have printed this object
331      already.  If so print a sharing indicator and just return; otherwise add
332      the object to the table for the next time (as long as it's a potential
333      source of sharing) and go on.
334 
335      A special case: ignore AST sharing when printing.  Sharing sub-ASTs,
336      particularly literals and variables, is harmless, and ASTs must not be
337      circular anyway: if they were, we'd in trouble for reasons much worse than
338      printing.  Shared literal *values* inside ASTs are treated as always.  We
339      still keep track of ASTs in the table, since the information may be needed
340      for an output notation when we print shared structures in an explicit way
341      in the future. */
342   if (JITTERLISP_IS_AST(o))
343     {
344       if (! jitterlisp_sharing_table_has (st, o))
345         jitterlisp_sharing_table_add (st, o);
346     }
347   else if (jitterlisp_sharing_table_has (st, o))
348     {
349       jitterlisp_begin_class (cx, "circular");
350       jitter_print_char_star (cx, "...");
351       jitterlisp_end_class (cx);
352       return;
353     }
354   else
355     jitterlisp_sharing_table_add (st, o);
356 
357   /* Print the object according to its type. */
358   if (JITTERLISP_IS_FIXNUM(o))
359     {
360       jitter_int decoded = JITTERLISP_FIXNUM_DECODE(o);
361       jitterlisp_begin_class (cx, "fixnum");
362       jitter_print_long_long (cx, 10, decoded);
363       jitterlisp_end_class (cx);
364     }
365   else if (JITTERLISP_IS_UNIQUE(o))
366     {
367       jitter_uint index = JITTERLISP_UNIQUE_DECODE(o);
368       if (index < JITTERLISP_UNIQUE_OBJECT_NO)
369         {
370           jitterlisp_begin_class (cx, "unique");
371           jitter_print_char_star (cx,
372                                    jitterlisp_unique_object_names [index]);
373         }
374       else
375         {
376           jitterlisp_begin_class (cx, "invalid");
377           jitter_print_char_star (cx, "#<invalid-unique-object:");
378           jitter_print_long_long (cx, 10, index);
379           jitter_print_char (cx, '>');
380         }
381       jitterlisp_end_class (cx);
382     }
383   else if (JITTERLISP_IS_CHARACTER(o))
384     {
385       jitterlisp_begin_class (cx, "character");
386       jitter_int c = JITTERLISP_CHARACTER_DECODE(o);
387       jitterlisp_print_character_name (cx, c);
388       jitterlisp_end_class (cx);
389     }
390   else if (JITTERLISP_IS_SYMBOL(o))
391     {
392       struct jitterlisp_symbol *s = JITTERLISP_SYMBOL_DECODE(o);
393       if (s->name_or_NULL != NULL)
394         {
395           /* Print an interned symbol. */
396           jitterlisp_begin_class (cx, "interned-symbol");
397           jitter_print_char_star (cx, s->name_or_NULL);
398         }
399       else if (jitterlisp_settings.print_compact_uninterned_symbols)
400         {
401           /* Print an uninterned symbol in compact notation. */
402           jitterlisp_begin_class (cx, "uninterned-symbol");
403           jitter_print_char_star (cx, "#<u");
404           jitter_print_long_long (cx, 10, (jitter_long_long) s->index);
405           jitter_print_char_star (cx, ">");
406         }
407       else
408         {
409           /* Print an uninterned symbol in the default notation. */
410           jitterlisp_begin_class (cx, "uninterned-symbol");
411           jitter_print_char_star (cx, "#<uninterned:");
412           jitter_print_char_star (cx, "0x");
413           jitter_print_long_long (cx, 16, (jitter_uint) s);
414           jitter_print_char_star (cx, ">");
415         }
416       jitterlisp_end_class (cx);
417     }
418   else if (JITTERLISP_IS_COMPILED_CLOSURE(o))
419     {
420       struct jitterlisp_closure *c = JITTERLISP_CLOSURE_DECODE(o);
421       struct jitterlisp_compiled_closure *cc = & c->compiled;
422       jitterlisp_begin_class (cx, "closure");
423       jitter_print_char_star (cx, "#<compiled-closure ");
424       jitter_print_long_long (cx, 10, c->in_arity);
425       jitter_print_char_star (cx, "-ary");
426       jitter_print_char_star (cx, " nonlocals ");
427       jitterlisp_print_recursive (cx, st, cc->nonlocals);
428       jitter_print_char_star (cx, ">");
429       jitterlisp_end_class (cx);
430     }
431   else if (JITTERLISP_IS_INTERPRETED_CLOSURE(o))
432     {
433       struct jitterlisp_interpreted_closure * const ic
434         = & JITTERLISP_CLOSURE_DECODE(o)->interpreted;
435       jitterlisp_begin_class (cx, "closure");
436       jitter_print_char_star (cx, "#<interpreted-closure ");
437       jitterlisp_print_recursive (cx, st, ic->environment);
438       jitter_print_char (cx, ' ');
439       jitterlisp_print_recursive (cx, st, ic->formals);
440       jitter_print_char (cx, ' ');
441       jitterlisp_print_recursive (cx, st, ic->body);
442       jitter_print_char (cx, '>');
443       jitterlisp_end_class (cx);
444     }
445   else if (JITTERLISP_IS_NON_PRIMITIVE_MACRO(o))
446     {
447       struct jitterlisp_interpreted_closure * const closure
448         = JITTERLISP_NON_PRIMITIVE_MACRO_DECODE(o);
449       jitterlisp_begin_class (cx, "non-primitive-macro");
450       jitter_print_char_star (cx, "#<macro ");
451       jitterlisp_print_recursive (cx, st, closure->environment);
452       jitter_print_char (cx, ' ');
453       jitterlisp_print_recursive (cx, st, closure->formals);
454       jitter_print_char (cx, ' ');
455       jitterlisp_print_recursive (cx, st, closure->body);
456       jitter_print_char_star (cx, ">");
457       jitterlisp_end_class (cx);
458     }
459   else if (JITTERLISP_IS_PRIMITIVE(o))
460     {
461       struct jitterlisp_primitive * const primitive
462         = JITTERLISP_PRIMITIVE_DECODE(o);
463       jitterlisp_begin_class (cx, "primitive");
464       jitter_print_char_star (cx, "#<primitive ");
465       jitter_print_char_star (cx, primitive->name);
466       jitter_print_char_star (cx, " ");
467       jitter_print_long_long (cx, 10, primitive->in_arity);
468       jitter_print_char_star (cx, "-ary>");
469       jitterlisp_end_class (cx);
470     }
471   else if (JITTERLISP_IS_PRIMITIVE_MACRO(o))
472     {
473       struct jitterlisp_primitive * const primitive
474         = JITTERLISP_PRIMITIVE_MACRO_DECODE(o);
475       jitterlisp_begin_class (cx, "primitive_macro");
476       jitter_print_char_star (cx, "#<primitive macro ");
477       jitter_print_char_star (cx, primitive->name);
478       jitter_print_char_star (cx, ">");
479       jitterlisp_end_class (cx);
480     }
481   else if (JITTERLISP_IS_BOX(o))
482     {
483       jitterlisp_begin_class (cx, "box");
484       jitter_print_char_star (cx, "#<box ");
485       jitterlisp_print_recursive (cx, st, JITTERLISP_EXP_B_A_GET(o));
486       jitter_print_char (cx, '>');
487       jitterlisp_end_class (cx);
488     }
489   else if (JITTERLISP_IS_CONS(o))
490     {
491       struct jitterlisp_cons * const c = JITTERLISP_CONS_DECODE(o);
492       jitterlisp_object car = c->car;
493       jitterlisp_object cdr = c->cdr;
494       jitterlisp_begin_class (cx, "cons");
495       jitter_print_char (cx, '(');
496       jitterlisp_print_recursive (cx, st, car);
497       jitterlisp_print_cdr (cx, st, cdr);
498       jitter_print_char (cx, ')');
499       jitterlisp_end_class (cx);
500     }
501   else if (JITTERLISP_IS_AST(o))
502     {
503       struct jitterlisp_ast * const ast = JITTERLISP_AST_DECODE(o);
504       jitterlisp_print_ast (cx, st, ast);
505     }
506   else if (JITTERLISP_IS_VECTOR(o))
507     {
508       const struct jitterlisp_vector * const v = JITTERLISP_VECTOR_DECODE(o);
509       jitterlisp_begin_class (cx, "vector");
510       jitter_print_char_star (cx, "#(");
511       int i;
512       int element_no = JITTERLISP_FIXNUM_DECODE(v->element_no);
513       for (i = 0; i < element_no; i ++)
514         {
515           jitterlisp_print_recursive (cx, st, v->elements [i]);
516           if (i < (element_no - 1))
517             jitter_print_char (cx, ' ');
518         }
519       jitter_print_char (cx, ')');
520       jitterlisp_end_class (cx);
521     }
522   else
523     {
524       jitterlisp_begin_class (cx, "invalid");
525       jitter_print_char_star (cx, "#<invalid-or-unknown>");
526       jitterlisp_end_class (cx);
527     }
528 }
529 
530 
531 
532 
533 /* Lisp object printer: user functions.
534  * ************************************************************************** */
535 
536 void
jitterlisp_print(jitter_print_context cx,jitterlisp_object o)537 jitterlisp_print (jitter_print_context cx, jitterlisp_object o)
538 {
539   /* Make a sharing table. */
540   struct jitter_hash_table st;
541   jitterlisp_sharing_table_initialize (& st);
542 
543   /* Print the object using the table. */
544   jitterlisp_print_recursive (cx, & st, o);
545 
546   /* We're done with the sharing table. */
547   jitterlisp_sharing_table_finalize (& st);
548 }
549 
550 char *
jitterlisp_print_to_string(jitterlisp_object o)551 jitterlisp_print_to_string (jitterlisp_object o)
552 {
553   /* Make a temporary memory print context. */
554   jitter_print_context cx = jitter_print_context_make_memory ();
555 
556   /* Print to it. */
557   jitterlisp_print (cx, o);
558 
559   /* Return a copy of the printed content into in already malloc-allocated
560      buffer.  Destroy the context, which does not destroy the copy. */
561   char *res = jitter_print_context_get_memory (cx, NULL);
562   jitter_print_context_destroy (cx);
563   return res;
564 }
565 
566 
567 
568 
569 /* Print error, warning and logging messages.
570  * ************************************************************************** */
571 
572 static void
jitterlisp_print_char_star_internal(char * class_suffix,const char * message)573 jitterlisp_print_char_star_internal (char *class_suffix, const char *message)
574 {
575   jitterlisp_begin_class (jitterlisp_print_context, class_suffix);
576   jitter_print_char_star (jitterlisp_print_context, message);
577   jitterlisp_end_class (jitterlisp_print_context);
578   jitter_print_flush (jitterlisp_print_context);
579 
580 }
581 static void
jitterlisp_print_internal(char * class_suffix,jitterlisp_object o)582 jitterlisp_print_internal (char *class_suffix, jitterlisp_object o)
583 {
584   jitterlisp_begin_class (jitterlisp_print_context, class_suffix);
585   jitterlisp_print (jitterlisp_print_context, o);
586   jitterlisp_end_class (jitterlisp_print_context);
587   jitter_print_flush (jitterlisp_print_context);
588 }
589 
590 void
jitterlisp_print_error_char_star(const char * message)591 jitterlisp_print_error_char_star (const char *message)
592 {
593   jitterlisp_print_char_star_internal ("error", message);
594 }
595 void
jitterlisp_print_error(jitterlisp_object o)596 jitterlisp_print_error (jitterlisp_object o)
597 {
598   jitterlisp_print_internal ("error", o);
599 }
600 void
jitterlisp_log_char_star(const char * message)601 jitterlisp_log_char_star (const char *message)
602 {
603   jitterlisp_print_char_star_internal ("log", message);
604 }
605 void
jitterlisp_log(jitterlisp_object o)606 jitterlisp_log (jitterlisp_object o)
607 {
608   jitterlisp_print_internal ("log", o);
609 }
610 
611 
612 
613 
614 
615 
616 /* Initialisation and finalisation.
617  * ************************************************************************** */
618 
619 /* The global print context */
620 jitter_print_context
621 jitterlisp_print_context = NULL /* For defensiveness's sake. */;
622 
623 void
jitterlisp_printer_initialize(void)624 jitterlisp_printer_initialize (void)
625 {
626   /* Initialise the GNU Libtextstyle wrapper, if used. */
627 #ifdef JITTER_WITH_LIBTEXTSTYLE
628   jitter_print_libtextstyle_initialize ();
629 #endif // #ifdef JITTER_WITH_LIBTEXTSTYLE
630 
631   /* If GNU Libtextstyle is used and colorisation is enabled, initialise the
632      print context using a Libtextstyle ostream... */
633 #ifdef JITTER_WITH_LIBTEXTSTYLE
634   if (jitterlisp_settings.colorize)
635     {
636       char *style_file_name = "jitterlisp-style.css"; // FIXME: this is barbaric.
637       styled_ostream_t ostream
638         = styled_ostream_create (STDOUT_FILENO, "(stdout)", TTYCTL_AUTO,
639                                  style_file_name);
640       jitterlisp_print_context
641         = jitter_print_context_make_libtextstyle (ostream);
642     }
643   else
644 #endif // #ifdef JITTER_WITH_LIBTEXTSTYLE
645     {
646       /* ...Otherwise use a non-styling context. */
647       jitterlisp_print_context
648         = jitter_print_context_make_file_star (stdout);
649     }
650 }
651 
652 void
jitterlisp_printer_finalize(void)653 jitterlisp_printer_finalize (void)
654 {
655   jitter_print_context_destroy (jitterlisp_print_context);
656   jitterlisp_print_context = NULL /* For defensiveness's sake. */;
657 
658 #ifdef JITTER_WITH_LIBTEXTSTYLE
659   jitter_print_libtextstyle_finalize ();
660 #endif // #ifdef JITTER_WITH_LIBTEXTSTYLE
661 }
662