/* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)printsym.c 1.12 8/10/83";

/*
 * Printing of symbolic information.
 */

#include "defs.h"
#include "symbols.h"
#include "languages.h"
#include "printsym.h"
#include "tree.h"
#include "eval.h"
#include "mappings.h"
#include "process.h"
#include "runtime.h"
#include "machine.h"
#include "names.h"
#include "main.h"

#ifndef public
#endif

/*
 * Maximum number of arguments to a function.
 * This is used as a check for the possibility that the stack has been
 * overwritten and therefore a saved argument pointer might indicate
 * to an absurdly large number of arguments.
 */

#define MAXARGSPASSED 20

/*
 * Return a pointer to the string for the name of the class that
 * the given symbol belongs to.
 */

private String clname[] = {
    "bad use", "constant", "type", "variable", "array", "fileptr",
    "record", "field", "procedure", "function", "funcvar",
    "ref", "pointer", "file", "set", "range", "label", "withptr",
    "scalar", "string", "program", "improper", "variant",
    "procparam", "funcparam", "module", "tag", "common", "typeref"
};

public String classname(s)
Symbol s;
{
    return clname[ord(s->class)];
}

/*
 * Note the entry of the given block, unless it's the main program.
 */

public printentry(s)
Symbol s;
{
    if (s != program) {
	printf("\nentering %s %s\n", classname(s), symname(s));
    }
}

/*
 * Note the exit of the given block
 */

public printexit(s)
Symbol s;
{
    if (s != program) {
	printf("leaving %s %s\n\n", classname(s), symname(s));
    }
}

/*
 * Note the call of s from t.
 */

public printcall(s, t)
Symbol s, t;
{
    printf("calling %s", symname(s));
    printparams(s, nil);
    printf(" from %s %s\n", classname(t), symname(t));
}

/*
 * Note the return from s.  If s is a function, print the value
 * it is returning.  This is somewhat painful, since the function
 * has actually just returned.
 */

public printrtn(s)
Symbol s;
{
    register Symbol t;
    register int len;
    Boolean isindirect;

    printf("returning ");
    if (s->class == FUNC && (!istypename(s->type,"void"))) {
	len = size(s->type);
	if (canpush(len)) {
	    t = rtype(s->type);
	    isindirect = (Boolean) (t->class == RECORD or t->class == VARNT);
	    pushretval(len, isindirect);
	    printval(s->type);
	    putchar(' ');
	} else {
	    printf("(value too large) ");
	}
    }
    printf("from %s\n", symname(s));
}

/*
 * Print the values of the parameters of the given procedure or function.
 * The frame distinguishes recursive instances of a procedure.
 */

public printparams(f, frame)
Symbol f;
Frame frame;
{
    Symbol param;
    int n, m, s;

    n = nargspassed(frame);
    param = f->chain;
    if (param != nil or n > 0) {
	printf("(");
	m = n;
	if (param != nil) {
	    for (;;) {
		s = size(param) div sizeof(Word);
		if (s == 0) {
		    s = 1;
		}
		m -= s;
		printv(param, frame);
		param = param->chain;
	    if (param == nil) break;
		printf(", ");
	    }
	}
	if (m > 0) {
	    if (m > MAXARGSPASSED) {
		m = MAXARGSPASSED;
	    }
	    if (f->chain != nil) {
		printf(", ");
	    }
	    for (;;) {
		--m;
		printf("0x%x", argn(n - m, frame));
	    if (m <= 0) break;
		printf(", ");
	    }
	}
	printf(")");
    }
}

/*
 * Test if a symbol should be printed.  We don't print files,
 * for example, simply because there's no good way to do it.
 * The symbol must be within the given function.
 */

public Boolean should_print(s)
Symbol s;
{
    Boolean b;
    register Symbol t;

    switch (s->class) {
	case VAR:
	case FVAR:
	    if (isparam(s)) {
		b = false;
	    } else {
		t = rtype(s->type);
		if (t == nil) {
		    b = false;
		} else {
		    switch (t->class) {
			case FILET:
			case SET:
			case BADUSE:
			    b = false;
			    break;

			default:
			    b = true;
			    break;
		    }
		}
	    }
	    break;

	default:
	    b = false;
	    break;
    }
    return b;
}

/*
 * Print the name and value of a variable.
 */

public printv(s, frame)
Symbol s;
Frame frame;
{
    Address addr;
    int len;

    if (isambiguous(s) and ismodule(container(s))) {
	printname(stdout, s);
	printf(" = ");
    } else {
	printf("%s = ", symname(s));
    }
    if(s->type->class == ARRAY && (! istypename(s->type->type,"char")) ) {
	printf(" ARRAY ");
    } else {
       if (isvarparam(s)) {
	   rpush(address(s, frame), sizeof(Address));
	   addr = pop(Address);
	   len = size(s->type);
       } else {
	   addr = address(s, frame);
	   len = size(s);
       }
       if (canpush(len)) {
	   rpush(addr, len);
	   printval(s->type);
       } else {
	   printf("*** expression too large ***");
       }
   }
}

/*
 * Print out the name of a symbol.
 */

public printname(f, s)
File f;
Symbol s;
{
    if (s == nil) {
	fprintf(f, "(noname)");
    } else if (isredirected() or isambiguous(s)) {
	printwhich(f, s);
    } else {
	fprintf(f, "%s", symname(s));
    }
}

/*
 * Print the fully specified variable that is described by the given identifer.
 */

public printwhich(f, s)
File f;
Symbol s;
{
    printouter(f, container(s));
    fprintf(f, "%s", symname(s));
}

/*
 * Print the fully qualified name of each symbol that has the same name
 * as the given symbol.
 */

public printwhereis(f, s)
File f;
Symbol s;
{
    register Name n;
    register Symbol t;

    checkref(s);
    n = s->name;
    t = lookup(n);
    printwhich(f, t);
    t = t->next_sym;
    while (t != nil) {
	if (t->name == n) {
	    putc(' ', f);
	    printwhich(f, t);
	}
	t = t->next_sym;
    }
    putc('\n', f);
}

private printouter(f, s)
File f;
Symbol s;
{
    Symbol outer;

    if (s != nil) {
	outer = container(s);
	if (outer != nil and outer != program) {
	    printouter(f, outer);
	}
	fprintf(f, "%s.", symname(s));
    }
}

public printdecl(s)
Symbol s;
{
    checkref(s);
    (*language_op(s->language, L_PRINTDECL))(s);
}

/*
 * Straight dump of symbol information.
 */

public psym(s)
Symbol s;
{
    printf("name\t%s\n", symname(s));
    printf("lang\t%s\n", language_name(s->language));
    printf("level\t%d\n", s->level);
    printf("class\t%s\n", classname(s));
    printf("type\t0x%x", s->type);
    if (s->type != nil and s->type->name != nil) {
	printf(" (%s)", symname(s->type));
    }
    printf("\nchain\t0x%x", s->chain);
    if (s->chain != nil and s->chain->name != nil) {
	printf(" (%s)", symname(s->chain));
    }
    printf("\nblock\t0x%x", s->block);
    if (s->block->name != nil) {
	printf(" (");
	printname(stdout, s->block);
	putchar(')');
    }
    putchar('\n');
    switch (s->class) {
	case VAR:
	case REF:
	    if (s->level >= 3) {
		printf("address\t0x%x\n", s->symvalue.offset);
	    } else {
		printf("offset\t%d\n", s->symvalue.offset);
	    }
	    printf("size\t%d\n", size(s));
	    break;

	case RECORD:
	case VARNT:
	    printf("size\t%d\n", s->symvalue.offset);
	    break;

	case FIELD:
	    printf("offset\t%d\n", s->symvalue.field.offset);
	    printf("size\t%d\n", s->symvalue.field.length);
	    break;

	case PROG:
	case PROC:
	case FUNC:
	    printf("address\t0x%x\n", s->symvalue.funcv.beginaddr);
	    if (isinline(s)) {
		printf("inline procedure\n");
	    }
	    if (nosource(s)) {
		printf("does not have source information\n");
	    } else {
		printf("has source information\n");
	    }
	    break;

	case RANGE:

	    prangetype(s->symvalue.rangev.lowertype);
	    printf("lower\t%d\n", s->symvalue.rangev.lower);
	    prangetype(s->symvalue.rangev.uppertype);
	    printf("upper\t%d\n", s->symvalue.rangev.upper);
	    break;

	default:
	    /* do nothing */
	    break;
    }
}

private prangetype(r)
Rangetype r;
{
    switch (r) {
	case R_CONST:
	    printf("CONST");
	    break;

	case R_ARG:
	    printf("ARG");
	    break;

	case R_TEMP:
	    printf("TEMP");
	    break;

	case R_ADJUST:
	    printf("ADJUST");
	    break;
    }
}

/*
 * Print out the value on top of the stack according to the given type.
 */

public printval(t)
Symbol t;
{
    Symbol s;

    checkref(t);
    switch (t->class) {
	case PROC:
	case FUNC:
	    s = pop(Symbol);
	    printf("%s", symname(s));
	    break;

	default:
	    if (t->language == nil) {
		error("unknown language");
	    } else {
		(*language_op(t->language, L_PRINTVAL))(t);
	    }
	    break;
    }
}

/*
 * Print out the value of a record, field by field.
 */

public printrecord(s)
Symbol s;
{
    if (s->chain == nil) {
	error("record has no fields");
    }
    printf("(");
    sp -= size(s);
    printfield(s->chain);
    printf(")");
}

/*
 * Print out a field, first printing out other fields.
 * This is done because the fields are chained together backwards.
 */

private printfield(s)
Symbol s;
{
    Stack *savesp;

    if (s->chain != nil) {
	printfield(s->chain);
	printf(", ");
    }
    printf("%s = ", symname(s));
    savesp = sp;
    sp += ((s->symvalue.field.offset div BITSPERBYTE) + size(s->type));
    printval(s);
    sp = savesp;
}

/*
 * Print out the contents of an array.
 * Haven't quite figured out what the best format is.
 *
 * This is rather inefficient.
 *
 * The "2*elsize" is there since "printval" drops the stack by elsize.
 */

public printarray(a)
Symbol a;
{
    Stack *savesp, *newsp;
    Symbol eltype;
    long elsize;
    String sep;

    savesp = sp;
    sp -= (size(a));
    newsp = sp;
    eltype = rtype(a->type);
    elsize = size(eltype);
    printf("(");
    if (eltype->class == RECORD or eltype->class == ARRAY or
      eltype->class == VARNT) {
	sep = "\n";
	putchar('\n');
    } else {
	sep = ", ";
    }
    for (sp += elsize; sp <= savesp; sp += 2*elsize) {
	if (sp - elsize != newsp) {
	    fputs(sep, stdout);
	}
	printval(eltype);
    }
    sp = newsp;
    if (streq(sep, "\n")) {
	putchar('\n');
    }
    printf(")");
}

/*
 * Print out the value of a real number in Pascal notation.
 * This is, unfortunately, different than what one gets
 * from "%g" in printf.
 */

public prtreal(r)
double r;
{
    extern char *index();
    char buf[256];

    sprintf(buf, "%g", r);
    if (buf[0] == '.') {
	printf("0%s", buf);
    } else if (buf[0] == '-' and buf[1] == '.') {
	printf("-0%s", &buf[1]);
    } else {
	printf("%s", buf);
    }
    if (index(buf, '.') == nil) {
	printf(".0");
    }
}

/*
 * Print out a character using ^? notation for unprintables.
 */

public printchar(c)
char c;
{
    if (c == 0) {
	putchar('\\');
	putchar('0');
    } else if (c == '\n') {
	putchar('\\');
	putchar('n');
    } else if (c > 0 and c < ' ') {
	putchar('^');
	putchar(c - 1 + 'A');
    } else {
	putchar(c);
    }
}
