/* CURRENTLY BEING EDITED. LOOK AT .html VERSION FOR LAST FROZEN ONE */ /* BACK IN 10 MINS... */ #include <stdio.h> #include <stdlib.h> static int debug = (0!=0); static int vertical = (0==0); static int horizontal = (0==0); static int wide = (0!=0); static int trim = (0==0); static int testone = (0==0); static char *progname; typedef struct { char *op; // '+', '-', '*', '/', etc., or terminal. int left; // -1 means no child int right; // ditto } CELL; static CELL ast[256]; // abstract syntax tree represented by a tree of // cells // row col long pic[256][256]; // 0..255 is char, >= 256 is ptr to string void mknode(int nodeno, char *op, int left, int right) { ast[nodeno].op = op; ast[nodeno].left = left; ast[nodeno].right = right; } int oldtextblit(int row, int col, char *src) { // post-processing string expansion int l = 0; for (;;) { if (*src == '\0') break; if (debug) fprintf(stderr, "1: Planting '%c' at [%d][%d]\n", *src, row, col); pic[row][col++] = *src++; l += 1; } return l; } int textblit(int row, int col, char *src) { // store pointer to string, unpack later on output int l = strlen(src); pic[row][col] = (int)src; return (l+(wide?3:1))>>(wide?2:1); // half size because on diagonal } void layout(int id, int idx, int rowoff, int coloff, int *depth, int *width) { char *operator; int leftchild, rightchild; int leftchilddepth = 0, leftchildwidth = 0; int rightchilddepth = 0, rightchildwidth = 0; int deltadepth = 0, deltawidth = 0; int i; if (debug) fprintf(stderr, ">> %d:layout(%d, rowcol[%d][%d], depth %d, width %d);\n", id, idx, rowoff, coloff, *depth, *width); operator = ast[idx].op; leftchild = ast[idx].left; rightchild = ast[idx].right; // Anchor the corner node. (void)textblit(rowoff, coloff, operator); /* not strcpy - don't copy NUL */ deltawidth = 1; if (rightchild >= 0) { int len = ((strlen(ast[leftchild].op)+(wide?3:1))>>(wide?2:1))+1; // text on the diagonal while (len-- > 1) {deltawidth += 1; pic[rowoff][coloff-1+deltawidth] = '\\';} // attach the RHS tree if (debug) fprintf(stderr, "Recursing to right node %d\n", rightchild); layout(2*id, rightchild, rowoff, coloff+deltawidth, &rightchilddepth, &rightchildwidth); deltadepth = rightchilddepth; } else { deltadepth = 1; /* The op itself */ } // testing: correcting a typo if (((strlen(operator)+(wide?3:1))>>(wide?2:1)) >= deltawidth) deltawidth = ((strlen(operator)+(wide?3:1))>>(wide?2:1))+2; if (leftchild >= 0) { // draw the down link // calculate extra height if ((((strlen(ast[leftchild].op)+(wide?3:1))>>(wide?2:1))) > deltadepth) { deltadepth = ((strlen(ast[leftchild].op)+(wide?3:1))>>(wide?2:1)); } for (i = 1; i < deltadepth+1 /* +1 for spacer row */; i++) { if (debug) fprintf(stderr, "2: Planting '%c' at [%d][%d]\n", '/', rowoff+i, coloff); pic[rowoff+i][coloff] = '/'; } // recurse on the LHS tree if (debug) fprintf(stderr, "Recursing to left node %d\n", leftchild); layout(2*id+1, leftchild, rowoff+deltadepth+1, coloff, &leftchilddepth, &leftchildwidth); *depth = (*depth) + leftchilddepth + deltadepth + 1; } else *depth = (*depth) + deltadepth; if (rightchildwidth+deltawidth > leftchildwidth) { *width = (rightchildwidth+deltawidth); } else { *width = leftchildwidth; } if (debug) fprintf(stderr, "<< %d:layout(%d, rowcol[%d][%d], depth %d, width %d);\n", id, idx, rowoff, coloff, *depth, *width); } int main(int argc, char **argv) { int depth = 0, width = 0, row, col, offset, trimmable; // argc is 1 if no params given progname = argv[0]; for (;;) { if (argc == 1) break; argv += 1; argc -= 1; /* skip to next arg, in advance */ if (strcmp(argv[0], "-d") == 0) { debug = (0==0); } else if (strcmp(argv[0], "-nv") == 0) { vertical = (0!=0); } else if (strcmp(argv[0], "-nh") == 0) { horizontal = (0!=0); } else if (strcmp(argv[0], "-nt") == 0) { trim = (0!=0); } else if (strcmp(argv[0], "-w") == 0) { wide = (0==0); } else if (strcmp(argv[0], "-t1") == 0) { testone = (0==0); } else if (strcmp(argv[0], "-t2") == 0) { testone = (0!=0); } else if (strcmp(argv[0], "-h") == 0) { fprintf(stderr, "%s:\n", progname); fprintf(stderr, " -nh No Horizontal shear\n"); fprintf(stderr, " -nv No Vertical shear\n"); fprintf(stderr, " -nt No Trim\n"); fprintf(stderr, " -w Wide-format display\n"); fprintf(stderr, " -t1 Internal Test 1\n"); fprintf(stderr, " -t2 Internal Test 2\n"); fprintf(stderr, " -d Debug\n"); fprintf(stderr, " arg Use tree desc from file if given,\n"); fprintf(stderr, " otherwise internal test\n"); exit(0); } else if (*argv[0] == '-') { fprintf(stderr, "syntax: %s [-n(v|h)] [-w] [-t(1|2)] filename\?\n", progname); exit(0); } else { argc += 1; argv -= 1; break; } } if (argc == 1) { // internal test wanted - we can do that :-) } else if (argc == 2) { // argv[1] is now the actual argument, if present. fprintf(stderr, "%s: reading from file not yet implemented\n", progname); exit(1); } else { fprintf(stderr, "%s: spurious parameter %s\n", progname, argv[2]); exit(1); } // Init. for (col = 0; col < 256; col++) { for (row = 0; row < 256; row++) { pic[row][col] = ' '; } } /* build example tree - this will actually be done by a parser */ if (testone) { mknode(1, "[]", 5, 6); mknode(0, "=", 1, 2); mknode(2, "+", 3, 4); mknode(3, "longvarname", -1, -1); mknode(4, "->", 7, 8); mknode(5, "arrayname", -1, -1); mknode(6, "5", -1, -1); mknode(7, "structname", -1, -1); mknode(8, "fieldname", -1, -1); } else { mknode(1, "[]", 5, 6); mknode(0, "=", 1, 2); mknode(2, "+", 3, 4); mknode(3, "a", -1, -1); mknode(4, "->", 7, 8); mknode(5, "b", -1, -1); mknode(6, "5", -1, -1); mknode(7, "c", -1, -1); mknode(8, "d", -1, -1); } /* Generate layout */ layout(1, 0, 128, 0, &depth, &width); if (debug) fprintf(stderr, "Dump layout: rows = %d cols = %d\n", depth, width); if (debug) fflush(stderr); if (vertical) { /* apply vertical shear first */ offset = 1; for (col = 1; col < 256; col++) { // move this column down by 'offset' for (row = 255; row > offset; row--) { pic[row][col] = pic[row-offset][col]; pic[row-offset][col] = ' '; } offset += 1; } } if (horizontal) { /* apply horizontal shear next */ row = 255; // start at bottom of drawing offset = 0; for (;;) { static long temp[1024]; for (col = 0; col < 256; col++) { temp[col] = ' '; } for (col = 0; col < 256; col++) { temp[col*2+offset] = pic[row][col]; temp[col*2+offset+1] = ' '; } for (col = 0; col < 256; col++) { pic[row][col] = temp[col]; } if (row == 0) break; offset += 1; /* more shear on next row up */ row -= 1; } } if (trim) { trimmable = (0==0); for (;;) { for (row = 0; row < 256; row++) { if (pic[row][0] != ' ') { trimmable = (0!=0); break; } } if (!trimmable) break; for (row = 0; row < 256; row++) { for (col = 0; col+1 < 256; col++) { pic[row][col] = pic[row][col+1]; } pic[row][255] = ' '; } } } if (wide) { /* apply widening last */ row = 255; // start at bottom of drawing offset = 0; for (;;) { static long temp[1024]; for (col = 0; col < 256; col++) { temp[col] = ' '; } for (col = 0; col < 256; col++) { temp[col*2+offset] = pic[row][col]; temp[col*2+offset+1] = ' '; } for (col = 0; col < 256; col++) { pic[row][col] = temp[col]; } if (row == 0) break; row -= 1; } } /* display tree */ for (row = 0; row < 256; row++) { trimmable = (0 == 0); for (col = 0; col < 256; col++) { if (pic[row][col] != ' ') { trimmable = (0!=0); break; } } if (!trimmable) { fprintf(stdout, " "); // INDENT for (col = 255; col >= 0; col--) { if (pic[row][col] != ' ') break; pic[row][col] = '\0'; } for (col = 0; col < 256; col++) { if ((pic[row][col] < -128) || (pic[row][col] > 255)) { oldtextblit(row, col, (char *)pic[row][col]); } else if (pic[row][col] == '\0') break; putchar(pic[row][col]); } putchar('\n'); } } /* Output from the above is: = / \ / + / / \ / / \ / / \ / / \ / / \ / / \ / / -> [] longvarname / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ structname fieldname arrayname 5 Short test produces: = / \ / + / / \ / / -> / / / \ [] a c d / \ b 5 */ fflush(stdout); exit(0); return 0; }