/* A generic pretty printer. Copyright (C) 2005 The MITRE Corporation Author: John D. Ramsdell -- December 2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See pprint.h for a description of the interface. The alogithm is by Lawrence C. Paulson, who simplified an algorithm by Derek C. Oppen. Derek C. Oppen, Prettyprinting, ACM Transactions on Programming Languages and Systems, Vol 2, No. 4, October 1980, Pages 465-483. The pretty printer is based on ML programs with the following copyright: (**** ML Programs from Chapter 8 of ML for the Working Programmer, 2nd edition by Lawrence C. Paulson, Computer Laboratory, University of Cambridge. (Cambridge University Press, 1996) Copyright (C) 1996 by Cambridge University Press. Permission to copy without fee is granted provided that this copyright notice and the DISCLAIMER OF WARRANTY are included in any copy. DISCLAIMER OF WARRANTY. These programs are provided `as is' without warranty of any kind. We make no warranties, express or implied, that the programs are free of error, or are consistent with any particular standard of merchantability, or that they will meet your requirements for any particular application. They should not be relied upon for solving a problem whose incorrect solution could result in injury to a person or loss of property. If you do use the programs or functions in such a manner, it is at your own risk. The author and publisher disclaim all liability for direct, incidental or consequential damages resulting from your use of these programs or functions. ****) */ #include #include #include #include #include "pyval.h" typedef struct pretty *pretty_t; static void * xmalloc(size_t size) { void *value = malloc(size); if (!value) { fprintf(stderr, "Memory exhausted--malloc failed\n"); exit(1); } return value; } typedef struct env { int space; int margin; FILE *out; } *env_t; struct pretty { int length; int (*break_it)(env_t, pretty_t, int); void (*print_it)(env_t, pretty_t, int, int, int); union { const char *string; struct { int indent; pretty_t p; } blk; } u; pretty_t next; }; static struct pretty error_marker[1]; /* Allocation using obstack */ #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free static struct obstack stack[1]; static void pprint_init(void) { obstack_init(stack); } static pretty_t alloc(void) { return (pretty_t)obstack_alloc(stack, sizeof(struct pretty)); } /* Helpers */ static void blanks(env_t e, int n) { for (; n > 0; n--) { fputc(' ', e->out); e->space--; } } static void newline(env_t e) { fputc('\n', e->out); e->space = e->margin; } static int break_dist(env_t e, pretty_t p, int after) { if (p) return p->break_it(e, p, after); else return after; } static void printing(env_t e, pretty_t p, int block_space, int after, int force_breaks) { for (; p; p = p->next) p->print_it(e, p, block_space, after, force_breaks); } /* Strings */ static int str_break_it(env_t e, pretty_t p, int after) { return p->length + break_dist(e, p->next, after); } static void str_print_it(env_t e, pretty_t p, int block_space, int after, int force_breaks) { fputs(p->u.string, e->out); e->space -= p->length; } static pretty_t pstring(const char *string, pretty_t next) { if (string && next != error_marker) { pretty_t p = alloc(); if (!p) return error_marker; p->length = strlen(string); p->break_it = str_break_it; p->print_it = str_print_it; p->u.string = string; p->next = next; return p; } else return error_marker; } /* Unbreakable space */ static void spc_print_it(env_t e, pretty_t p, int block_space, int after, int force_breaks) { int n = p->length; e->space -= n; while (n-- > 0) fputc(' ', e->out); } static pretty_t pspace(int fill, pretty_t next) { if (fill >= 0 && next != error_marker) { pretty_t p = alloc(); if (!p) return error_marker; p->length = fill; p->break_it = str_break_it; p->print_it = spc_print_it; p->next = next; return p; } else return error_marker; } /* Breaks */ static int brk_break_it(env_t e, pretty_t p, int after) { return 0; } static void brk_print_it(env_t e, pretty_t p, int block_space, int after, int force_breaks) { int len = p->length; if (!force_breaks && len + break_dist(e, p->next, after) <= e->space) blanks(e, len); else { newline(e); blanks(e, e->margin - block_space); } } static pretty_t pbreak(int skip, pretty_t next) { if (skip >= 0 && next != error_marker) { pretty_t p = alloc(); if (!p) return error_marker; p->length = skip; p->break_it = brk_break_it; p->print_it = brk_print_it; p->next = next; return p; } else return error_marker; } /* Blocks */ static int sum_length(pretty_t p) { int sum = 0; for (; p; p = p->next) sum += p->length; return sum; } static void blk_print_it(env_t e, pretty_t p, int block_space, int after, int force_breaks) { int dist = break_dist(e, p->next, after); printing(e, p->u.blk.p, e->space - p->u.blk.indent, dist, 0); } static pretty_t pblock(int indent, pretty_t p, pretty_t next) { if (p != error_marker && next != error_marker) { pretty_t r = alloc(); if (!r) return error_marker; r->length = sum_length(p); r->break_it = str_break_it; r->print_it = blk_print_it; r->u.blk.indent = indent; r->u.blk.p = p; r->next = next; return r; } else return error_marker; } /* Groups */ static void grp_print_it(env_t e, pretty_t p, int block_space, int after, int force_breaks) { int dist = break_dist(e, p->next, after); force_breaks = p->length + dist > e->space; printing(e, p->u.blk.p, e->space - p->u.blk.indent, dist, force_breaks); } static pretty_t pgroup(int indent, pretty_t p, pretty_t next) { if (p != error_marker && next != error_marker) { pretty_t r = alloc(); if (!r) return error_marker; r->length = sum_length(p); r->break_it = str_break_it; r->print_it = grp_print_it; r->u.blk.indent = indent; r->u.blk.p = p; r->next = next; return r; } else return error_marker; } /* the pretty printer */ static int pprint(FILE *out, pretty_t pretty, int margin) { if (pretty == error_marker) { obstack_free(stack, 0); pprint_init(); return -1; } else { struct env e = { margin, margin, out }; printing(&e, pretty, margin, 0, 0); obstack_free(stack, 0); pprint_init(); return 0; } } /* The Python stuff */ static const char *pyfun = "pyval"; void pyval_init(const char *string) { pyfun = string; pprint_init(); } typedef enum { PYVAL_STRING, PYVAL_ENTRY } pyval_type_t; struct pyval { pyval_type_t type; const char *string; pyval_t value; pyval_t next; }; static pyval_t pyalloc(void) { return (pyval_t)obstack_alloc(stack, sizeof(struct pyval)); } pyval_t pyval_string(const char *string) { if (!string) return 0; pyval_t pyval = pyalloc(); pyval->type = PYVAL_STRING; pyval->string = string; pyval->value = 0; pyval->next = 0; return pyval; } static struct pyval empty_dictionary[1] = { {PYVAL_ENTRY, 0, 0, 0} }; pyval_t pyval_dictionary(void) { return empty_dictionary; } pyval_t pyval_entry(const char *string, pyval_t value, pyval_t next) { if (!string || !value || !next || next->type != PYVAL_ENTRY) return 0; pyval_t pyval = pyalloc(); pyval->type = PYVAL_ENTRY; pyval->string = string; pyval->value = value; pyval->next = next; return pyval; } /* Python strings */ static size_t pyval_strlen(const char *s) { size_t n = 2; for (; *s; s++) { switch (*s) { case '\'': case '\\': case '\n': n += 2; break; default: n++; } } return n; } static void pyval_str_print_it(env_t e, pretty_t p, int block_space, int after, int force_breaks) { const char *s = p->u.string; fputc('\'', e->out); for (; *s; s++) { switch (*s) { case '\'': case '\\': case '\n': fputc('\\', e->out); } fputc(*s, e->out); } fputc('\'', e->out); e->space -= p->length; } static pretty_t pyval_pstring(const char *string, pretty_t next) { if (string && next != error_marker) { pretty_t p = alloc(); if (!p) return error_marker; p->length = pyval_strlen(string); p->break_it = str_break_it; p->print_it = pyval_str_print_it; p->u.string = string; p->next = next; return p; } else return error_marker; } static pretty_t pyval_pp(pyval_t, pretty_t); static pretty_t pyval_dict(pyval_t pyval, pretty_t next) { pretty_t p; do { if (next == error_marker) return error_marker; switch (pyval->type) { case PYVAL_STRING: return error_marker; case PYVAL_ENTRY: if (next) next = pstring(",", pbreak(1, next)); else next = pstring("}", next); p = pyval_pp(pyval->value, 0); p = pstring(":", pbreak(1, p)); p = pyval_pstring(pyval->string, p); next = pblock(1, p, next); pyval = pyval->next; if (!pyval) return error_marker; break; default: return error_marker; } } while (pyval != empty_dictionary); return next; } static pretty_t pyval_pp(pyval_t pyval, pretty_t next) { if (next == error_marker || !pyval) return error_marker; switch (pyval->type) { case PYVAL_STRING: return pyval_pstring(pyval->string, next); case PYVAL_ENTRY: if (pyval == empty_dictionary) return pstring("{}", next); else return pblock(1, pstring("{", pyval_dict(pyval, 0)), next); default: return error_marker; } } int pyval_print(FILE *out, pyval_t pyval, int margin) { pretty_t p = pyval_pp(pyval, pstring(")", 0)); p = pstring(pyfun, pstring("(", p)); return pprint(out, p, margin); }