diff options
Diffstat (limited to 'src/cmd/gc/fmt.c')
-rw-r--r-- | src/cmd/gc/fmt.c | 1639 |
1 files changed, 1639 insertions, 0 deletions
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c new file mode 100644 index 000000000..5672c0010 --- /dev/null +++ b/src/cmd/gc/fmt.c @@ -0,0 +1,1639 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include <libc.h> +#include "go.h" +#include "opnames.h" + +// +// Format conversions +// %L int Line numbers +// +// %E int etype values (aka 'Kind') +// +// %O int Node Opcodes +// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg) +// +// %J Node* Node details +// Flags: "%hJ" supresses things not relevant until walk. +// +// %V Val* Constant values +// +// %S Sym* Symbols +// Flags: +,- #: mode (see below) +// "%hS" unqualified identifier in any mode +// "%hhS" in export mode: unqualified identifier if exported, qualified if not +// +// %T Type* Types +// Flags: +,- #: mode (see below) +// 'l' definition instead of name. +// 'h' omit "func" and receiver in function types +// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix. +// +// %N Node* Nodes +// Flags: +,- #: mode (see below) +// 'h' (only in +/debug mode) suppress recursion +// 'l' (only in Error mode) print "foo (type Bar)" +// +// %H NodeList* NodeLists +// Flags: those of %N +// ',' separate items with ',' instead of ';' +// +// %Z Strlit* String literals +// +// In mparith1.c: +// %B Mpint* Big integers +// %F Mpflt* Big floats +// +// %S, %T and %N obey use the following flags to set the format mode: +enum { + FErr, // error mode (default) + FDbg, // "%+N" debug mode + FExp, // "%#N" export mode + FTypeId, // "%-N" turning-types-into-symbols-mode: identical types give identical strings +}; +static int fmtmode; +static int fmtpkgpfx; // %uT stickyness +// +// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode. +// +// The mode flags +, - and # are sticky, meaning they persist through +// recursions of %N, %T and %S, but not the h and l flags. The u flag is +// sticky only on %T recursions and only used in %-/Sym mode. + +// +// Useful format combinations: +// +// %+N %+H multiline recursive debug dump of node/nodelist +// %+hN %+hH non recursive debug dump +// +// %#N %#T export format +// %#lT type definition instead of name +// %#hT omit"func" and receiver in function signature +// +// %lN "foo (type Bar)" for error messages +// +// %-T type identifiers +// %-hT type identifiers without "func" and arg names in type signatures (methodsym) +// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash) +// + + +static int +setfmode(unsigned long *flags) +{ + int fm; + + fm = fmtmode; + if(*flags & FmtSign) + fmtmode = FDbg; + else if(*flags & FmtSharp) + fmtmode = FExp; + else if(*flags & FmtLeft) + fmtmode = FTypeId; + + *flags &= ~(FmtSharp|FmtLeft|FmtSign); + return fm; +} + +// Fmt "%L": Linenumbers +static int +Lconv(Fmt *fp) +{ + struct + { + Hist* incl; /* start of this include file */ + int32 idel; /* delta line number to apply to include */ + Hist* line; /* start of this #line directive */ + int32 ldel; /* delta line number to apply to #line */ + } a[HISTSZ]; + int32 lno, d; + int i, n; + Hist *h; + + lno = va_arg(fp->args, int32); + + n = 0; + for(h=hist; h!=H; h=h->link) { + if(h->offset < 0) + continue; + if(lno < h->line) + break; + if(h->name) { + if(h->offset > 0) { + // #line directive + if(n > 0 && n < HISTSZ) { + a[n-1].line = h; + a[n-1].ldel = h->line - h->offset + 1; + } + } else { + // beginning of file + if(n < HISTSZ) { + a[n].incl = h; + a[n].idel = h->line; + a[n].line = 0; + } + n++; + } + continue; + } + n--; + if(n > 0 && n < HISTSZ) { + d = h->line - a[n].incl->line; + a[n-1].ldel += d; + a[n-1].idel += d; + } + } + + if(n > HISTSZ) + n = HISTSZ; + + for(i=n-1; i>=0; i--) { + if(i != n-1) { + if(fp->flags & ~(FmtWidth|FmtPrec)) + break; + fmtprint(fp, " "); + } + if(debug['L'] || (fp->flags&FmtLong)) + fmtprint(fp, "%s/", pathname); + if(a[i].line) + fmtprint(fp, "%s:%d[%s:%d]", + a[i].line->name, lno-a[i].ldel+1, + a[i].incl->name, lno-a[i].idel+1); + else + fmtprint(fp, "%s:%d", + a[i].incl->name, lno-a[i].idel+1); + lno = a[i].incl->line - 1; // now print out start of this file + } + if(n == 0) + fmtprint(fp, "<epoch>"); + + return 0; +} + +static char* +goopnames[] = +{ + [OADDR] = "&", + [OADD] = "+", + [OADDSTR] = "+", + [OANDAND] = "&&", + [OANDNOT] = "&^", + [OAND] = "&", + [OAPPEND] = "append", + [OAS] = "=", + [OAS2] = "=", + [OBREAK] = "break", + [OCALL] = "function call", // not actual syntax + [OCAP] = "cap", + [OCASE] = "case", + [OCLOSE] = "close", + [OCOMPLEX] = "complex", + [OCOM] = "^", + [OCONTINUE] = "continue", + [OCOPY] = "copy", + [ODEC] = "--", + [ODELETE] = "delete", + [ODEFER] = "defer", + [ODIV] = "/", + [OEQ] = "==", + [OFALL] = "fallthrough", + [OFOR] = "for", + [OGE] = ">=", + [OGOTO] = "goto", + [OGT] = ">", + [OIF] = "if", + [OIMAG] = "imag", + [OINC] = "++", + [OIND] = "*", + [OLEN] = "len", + [OLE] = "<=", + [OLSH] = "<<", + [OLT] = "<", + [OMAKE] = "make", + [OMINUS] = "-", + [OMOD] = "%", + [OMUL] = "*", + [ONEW] = "new", + [ONE] = "!=", + [ONOT] = "!", + [OOROR] = "||", + [OOR] = "|", + [OPANIC] = "panic", + [OPLUS] = "+", + [OPRINTN] = "println", + [OPRINT] = "print", + [ORANGE] = "range", + [OREAL] = "real", + [ORECV] = "<-", + [ORETURN] = "return", + [ORSH] = ">>", + [OSELECT] = "select", + [OSEND] = "<-", + [OSUB] = "-", + [OSWITCH] = "switch", + [OXOR] = "^", +}; + +// Fmt "%O": Node opcodes +static int +Oconv(Fmt *fp) +{ + int o; + + o = va_arg(fp->args, int); + if((fp->flags & FmtSharp) || fmtmode != FDbg) + if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil) + return fmtstrcpy(fp, goopnames[o]); + + if(o >= 0 && o < nelem(opnames) && opnames[o] != nil) + return fmtstrcpy(fp, opnames[o]); + + return fmtprint(fp, "O-%d", o); +} + +static const char* classnames[] = { + "Pxxx", + "PEXTERN", + "PAUTO", + "PPARAM", + "PPARAMOUT", + "PPARAMREF", + "PFUNC", +}; + +// Fmt "%J": Node details. +static int +Jconv(Fmt *fp) +{ + Node *n; + char *s; + int c; + + n = va_arg(fp->args, Node*); + + c = fp->flags&FmtShort; + + if(!c && n->ullman != 0) + fmtprint(fp, " u(%d)", n->ullman); + + if(!c && n->addable != 0) + fmtprint(fp, " a(%d)", n->addable); + + if(!c && n->vargen != 0) + fmtprint(fp, " g(%d)", n->vargen); + + if(n->lineno != 0) + fmtprint(fp, " l(%d)", n->lineno); + + if(!c && n->xoffset != BADWIDTH) + fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta); + + if(n->class != 0) { + s = ""; + if(n->class & PHEAP) s = ",heap"; + if((n->class & ~PHEAP) < nelem(classnames)) + fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s); + else + fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s); + } + + if(n->colas != 0) + fmtprint(fp, " colas(%d)", n->colas); + + if(n->funcdepth != 0) + fmtprint(fp, " f(%d)", n->funcdepth); + + switch(n->esc) { + case EscUnknown: + break; + case EscHeap: + fmtprint(fp, " esc(h)"); + break; + case EscScope: + fmtprint(fp, " esc(s)"); + break; + case EscNone: + fmtprint(fp, " esc(no)"); + break; + case EscNever: + if(!c) + fmtprint(fp, " esc(N)"); + break; + default: + fmtprint(fp, " esc(%d)", n->esc); + break; + } + + if(n->escloopdepth) + fmtprint(fp, " ld(%d)", n->escloopdepth); + + if(!c && n->typecheck != 0) + fmtprint(fp, " tc(%d)", n->typecheck); + + if(!c && n->dodata != 0) + fmtprint(fp, " dd(%d)", n->dodata); + + if(n->isddd != 0) + fmtprint(fp, " isddd(%d)", n->isddd); + + if(n->implicit != 0) + fmtprint(fp, " implicit(%d)", n->implicit); + + if(n->embedded != 0) + fmtprint(fp, " embedded(%d)", n->embedded); + + if(!c && n->used != 0) + fmtprint(fp, " used(%d)", n->used); + return 0; +} + +// Fmt "%V": Values +static int +Vconv(Fmt *fp) +{ + Val *v; + vlong x; + + v = va_arg(fp->args, Val*); + + switch(v->ctype) { + case CTINT: + return fmtprint(fp, "%B", v->u.xval); + case CTRUNE: + x = mpgetfix(v->u.xval); + if(' ' <= x && x < 0x80 && x != '\\' && x != '\'') + return fmtprint(fp, "'%c'", (int)x); + if(0 <= x && x < (1<<16)) + return fmtprint(fp, "'\\u%04ux'", (int)x); + if(0 <= x && x <= Runemax) + return fmtprint(fp, "'\\U%08llux'", x); + return fmtprint(fp, "('\\x00' + %B)", v->u.xval); + case CTFLT: + if((fp->flags & FmtSharp) || fmtmode == FExp) + return fmtprint(fp, "%F", v->u.fval); + return fmtprint(fp, "%#F", v->u.fval); + case CTCPLX: + if((fp->flags & FmtSharp) || fmtmode == FExp) + return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag); + return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag); + case CTSTR: + return fmtprint(fp, "\"%Z\"", v->u.sval); + case CTBOOL: + if( v->u.bval) + return fmtstrcpy(fp, "true"); + return fmtstrcpy(fp, "false"); + case CTNIL: + return fmtstrcpy(fp, "nil"); + } + return fmtprint(fp, "<%d>", v->ctype); +} + +// Fmt "%Z": escaped string literals +static int +Zconv(Fmt *fp) +{ + Rune r; + Strlit *sp; + char *s, *se; + int n; + + sp = va_arg(fp->args, Strlit*); + if(sp == nil) + return fmtstrcpy(fp, "<nil>"); + + s = sp->s; + se = s + sp->len; + while(s < se) { + n = chartorune(&r, s); + s += n; + switch(r) { + case Runeerror: + if(n == 1) { + fmtprint(fp, "\\x%02x", (uchar)*(s-1)); + break; + } + // fall through + default: + if(r < ' ') { + fmtprint(fp, "\\x%02x", r); + break; + } + fmtrune(fp, r); + break; + case '\t': + fmtstrcpy(fp, "\\t"); + break; + case '\n': + fmtstrcpy(fp, "\\n"); + break; + case '\"': + case '\\': + fmtrune(fp, '\\'); + fmtrune(fp, r); + break; + } + } + return 0; +} + +/* +s%,%,\n%g +s%\n+%\n%g +s%^[ ]*T%%g +s%,.*%%g +s%.+% [T&] = "&",%g +s%^ ........*\]%&~%g +s%~ %%g +*/ + +static char* +etnames[] = +{ + [TINT] = "INT", + [TUINT] = "UINT", + [TINT8] = "INT8", + [TUINT8] = "UINT8", + [TINT16] = "INT16", + [TUINT16] = "UINT16", + [TINT32] = "INT32", + [TUINT32] = "UINT32", + [TINT64] = "INT64", + [TUINT64] = "UINT64", + [TUINTPTR] = "UINTPTR", + [TFLOAT32] = "FLOAT32", + [TFLOAT64] = "FLOAT64", + [TCOMPLEX64] = "COMPLEX64", + [TCOMPLEX128] = "COMPLEX128", + [TBOOL] = "BOOL", + [TPTR32] = "PTR32", + [TPTR64] = "PTR64", + [TFUNC] = "FUNC", + [TARRAY] = "ARRAY", + [TSTRUCT] = "STRUCT", + [TCHAN] = "CHAN", + [TMAP] = "MAP", + [TINTER] = "INTER", + [TFORW] = "FORW", + [TFIELD] = "FIELD", + [TSTRING] = "STRING", + [TANY] = "ANY", +}; + +// Fmt "%E": etype +static int +Econv(Fmt *fp) +{ + int et; + + et = va_arg(fp->args, int); + if(et >= 0 && et < nelem(etnames) && etnames[et] != nil) + return fmtstrcpy(fp, etnames[et]); + return fmtprint(fp, "E-%d", et); +} + +// Fmt "%S": syms +static int +symfmt(Fmt *fp, Sym *s) +{ + char *p; + + if(s->pkg && !(fp->flags&FmtShort)) { + switch(fmtmode) { + case FErr: // This is for the user + if(s->pkg == localpkg) + return fmtstrcpy(fp, s->name); + // If the name was used by multiple packages, display the full path, + if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1) + return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name); + return fmtprint(fp, "%s.%s", s->pkg->name, s->name); + case FDbg: + return fmtprint(fp, "%s.%s", s->pkg->name, s->name); + case FTypeId: + if(fp->flags&FmtUnsigned) + return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash + return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym + case FExp: + if(s->pkg != builtinpkg) + return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name); + } + } + + if(fp->flags&FmtByte) { // FmtByte (hh) implies FmtShort (h) + // skip leading "type." in method name + p = utfrrune(s->name, '.'); + if(p) + p++; + else + p = s->name; + + // exportname needs to see the name without the prefix too. + if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg) + return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p); + + return fmtstrcpy(fp, p); + } + + return fmtstrcpy(fp, s->name); +} + +static char* +basicnames[] = +{ + [TINT] = "int", + [TUINT] = "uint", + [TINT8] = "int8", + [TUINT8] = "uint8", + [TINT16] = "int16", + [TUINT16] = "uint16", + [TINT32] = "int32", + [TUINT32] = "uint32", + [TINT64] = "int64", + [TUINT64] = "uint64", + [TUINTPTR] = "uintptr", + [TFLOAT32] = "float32", + [TFLOAT64] = "float64", + [TCOMPLEX64] = "complex64", + [TCOMPLEX128] = "complex128", + [TBOOL] = "bool", + [TANY] = "any", + [TSTRING] = "string", + [TNIL] = "nil", + [TIDEAL] = "ideal", + [TBLANK] = "blank", +}; + +static int +typefmt(Fmt *fp, Type *t) +{ + Type *t1; + Sym *s; + + if(t == T) + return fmtstrcpy(fp, "<T>"); + + if (t == bytetype || t == runetype) { + // in %-T mode collapse rune and byte with their originals. + if(fmtmode != FTypeId) + return fmtprint(fp, "%hS", t->sym); + t = types[t->etype]; + } + + if(t == errortype) + return fmtstrcpy(fp, "error"); + + // Unless the 'l' flag was specified, if the type has a name, just print that name. + if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) { + switch(fmtmode) { + case FTypeId: + if(fp->flags&FmtShort) + return fmtprint(fp, "%hS", t->sym); + if(fp->flags&FmtUnsigned) + return fmtprint(fp, "%uS", t->sym); + // fallthrough + case FExp: + if(t->sym->pkg == localpkg && t->vargen) + return fmtprint(fp, "%S·%d", t->sym, t->vargen); + break; + } + return fmtprint(fp, "%S", t->sym); + } + + if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) { + if(fmtmode == FErr && (t == idealbool || t == idealstring)) + fmtstrcpy(fp, "ideal "); + return fmtstrcpy(fp, basicnames[t->etype]); + } + + if(fmtmode == FDbg) + fmtprint(fp, "%E-", t->etype); + + switch(t->etype) { + case TPTR32: + case TPTR64: + if(fmtmode == FTypeId && (fp->flags&FmtShort)) + return fmtprint(fp, "*%hT", t->type); + return fmtprint(fp, "*%T", t->type); + + case TARRAY: + if(t->bound >= 0) + return fmtprint(fp, "[%d]%T", (int)t->bound, t->type); + if(t->bound == -100) + return fmtprint(fp, "[...]%T", t->type); + return fmtprint(fp, "[]%T", t->type); + + case TCHAN: + switch(t->chan) { + case Crecv: + return fmtprint(fp, "<-chan %T", t->type); + case Csend: + return fmtprint(fp, "chan<- %T", t->type); + } + + if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv) + return fmtprint(fp, "chan (%T)", t->type); + return fmtprint(fp, "chan %T", t->type); + + case TMAP: + return fmtprint(fp, "map[%T]%T", t->down, t->type); + + case TINTER: + fmtstrcpy(fp, "interface {"); + for(t1=t->type; t1!=T; t1=t1->down) + if(exportname(t1->sym->name)) { + if(t1->down) + fmtprint(fp, " %hS%hT;", t1->sym, t1->type); + else + fmtprint(fp, " %hS%hT ", t1->sym, t1->type); + } else { + // non-exported method names must be qualified + if(t1->down) + fmtprint(fp, " %uS%hT;", t1->sym, t1->type); + else + fmtprint(fp, " %uS%hT ", t1->sym, t1->type); + } + fmtstrcpy(fp, "}"); + return 0; + + case TFUNC: + if(fp->flags & FmtShort) { + fmtprint(fp, "%T", getinargx(t)); + } else { + if(t->thistuple) + fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t)); + else + fmtprint(fp, "func%T", getinargx(t)); + } + switch(t->outtuple) { + case 0: + break; + case 1: + if(fmtmode != FExp) { + fmtprint(fp, " %T", getoutargx(t)->type->type); // struct->field->field's type + break; + } + default: + fmtprint(fp, " %T", getoutargx(t)); + break; + } + return 0; + + case TSTRUCT: + if(t->funarg) { + fmtstrcpy(fp, "("); + if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags + for(t1=t->type; t1!=T; t1=t1->down) + if(t1->down) + fmtprint(fp, "%hT, ", t1); + else + fmtprint(fp, "%hT", t1); + } else { + for(t1=t->type; t1!=T; t1=t1->down) + if(t1->down) + fmtprint(fp, "%T, ", t1); + else + fmtprint(fp, "%T", t1); + } + fmtstrcpy(fp, ")"); + } else { + fmtstrcpy(fp, "struct {"); + for(t1=t->type; t1!=T; t1=t1->down) + if(t1->down) + fmtprint(fp, " %lT;", t1); + else + fmtprint(fp, " %lT ", t1); + fmtstrcpy(fp, "}"); + } + return 0; + + case TFIELD: + if(!(fp->flags&FmtShort)) { + s = t->sym; + // Take the name from the original, lest we substituted it with .anon%d + if (t->nname && (fmtmode == FErr || fmtmode == FExp)) + s = t->nname->orig->sym; + + if(s != S && !t->embedded) { + if(fp->flags&FmtLong) + fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg) + else + fmtprint(fp, "%S ", s); + } else if(fmtmode == FExp) { + // TODO(rsc) this breaks on the eliding of unused arguments in the backend + // when this is fixed, the special case in dcl.c checkarglist can go. + //if(t->funarg) + // fmtstrcpy(fp, "_ "); + //else + fmtstrcpy(fp, "? "); + } + } + + if(t->isddd) + fmtprint(fp, "...%T", t->type->type); + else + fmtprint(fp, "%T", t->type); + + if(!(fp->flags&FmtShort) && t->note) + fmtprint(fp, " \"%Z\"", t->note); + return 0; + + case TFORW: + if(t->sym) + return fmtprint(fp, "undefined %S", t->sym); + return fmtstrcpy(fp, "undefined"); + + case TUNSAFEPTR: + if(fmtmode == FExp) + return fmtprint(fp, "@\"unsafe\".Pointer"); + return fmtprint(fp, "unsafe.Pointer"); + } + + if(fmtmode == FExp) + fatal("missing %E case during export", t->etype); + // Don't know how to handle - fall back to detailed prints. + return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type); +} + +// Statements which may be rendered with a simplestmt as init. +static int +stmtwithinit(int op) +{ + switch(op) { + case OIF: + case OFOR: + case OSWITCH: + return 1; + } + return 0; +} + +static int +stmtfmt(Fmt *f, Node *n) +{ + int complexinit, simpleinit, extrablock; + + // some statements allow for an init, but at most one, + // but we may have an arbitrary number added, eg by typecheck + // and inlining. If it doesn't fit the syntax, emit an enclosing + // block starting with the init statements. + + // if we can just say "for" n->ninit; ... then do so + simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op); + // otherwise, print the inits as separate statements + complexinit = n->ninit && !simpleinit && (fmtmode != FErr); + // but if it was for if/for/switch, put in an extra surrounding block to limit the scope + extrablock = complexinit && stmtwithinit(n->op); + + if(extrablock) + fmtstrcpy(f, "{"); + + if(complexinit) + fmtprint(f, " %H; ", n->ninit); + + switch(n->op){ + case ODCL: + fmtprint(f, "var %S %T", n->left->sym, n->left->type); + break; + + case ODCLFIELD: + if(n->left) + fmtprint(f, "%N %N", n->left, n->right); + else + fmtprint(f, "%N", n->right); + break; + + case OAS: + if(n->colas && !complexinit) + fmtprint(f, "%N := %N", n->left, n->right); + else + fmtprint(f, "%N = %N", n->left, n->right); + break; + + case OASOP: + fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right); + break; + + case OAS2: + if(n->colas && !complexinit) { + fmtprint(f, "%,H := %,H", n->list, n->rlist); + break; + } + // fallthrough + case OAS2DOTTYPE: + case OAS2FUNC: + case OAS2MAPR: + case OAS2RECV: + fmtprint(f, "%,H = %,H", n->list, n->rlist); + break; + + case ORETURN: + fmtprint(f, "return %,H", n->list); + break; + + case OPROC: + fmtprint(f, "go %N", n->left); + break; + + case ODEFER: + fmtprint(f, "defer %N", n->left); + break; + + case OIF: + if(simpleinit) + fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody); + else + fmtprint(f, "if %N { %H }", n->ntest, n->nbody); + if(n->nelse) + fmtprint(f, " else { %H }", n->nelse); + break; + + case OFOR: + if(fmtmode == FErr) { // TODO maybe only if FmtShort, same below + fmtstrcpy(f, "for loop"); + break; + } + + fmtstrcpy(f, "for"); + if(simpleinit) + fmtprint(f, " %N;", n->ninit->n); + else if(n->nincr) + fmtstrcpy(f, " ;"); + + if(n->ntest) + fmtprint(f, " %N", n->ntest); + + if(n->nincr) + fmtprint(f, "; %N", n->nincr); + else if(simpleinit) + fmtstrcpy(f, ";"); + + + fmtprint(f, " { %H }", n->nbody); + break; + + case ORANGE: + if(fmtmode == FErr) { + fmtstrcpy(f, "for loop"); + break; + } + + fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody); + break; + + case OSELECT: + case OSWITCH: + if(fmtmode == FErr) { + fmtprint(f, "%O statement", n->op); + break; + } + + fmtprint(f, "%#O", n->op); + if(simpleinit) + fmtprint(f, " %N;", n->ninit->n); + if(n->ntest) + fmtprint(f, "%N", n->ntest); + + fmtprint(f, " { %H }", n->list); + break; + + case OCASE: + case OXCASE: + if(n->list) + fmtprint(f, "case %,H: %H", n->list, n->nbody); + else + fmtprint(f, "default: %H", n->nbody); + break; + + case OBREAK: + case OCONTINUE: + case OGOTO: + case OFALL: + case OXFALL: + if(n->left) + fmtprint(f, "%#O %N", n->op, n->left); + else + fmtprint(f, "%#O", n->op); + break; + + case OEMPTY: + break; + + case OLABEL: + fmtprint(f, "%N: ", n->left); + break; + + } + + if(extrablock) + fmtstrcpy(f, "}"); + + return 0; +} + + +static int opprec[] = { + [OAPPEND] = 8, + [OARRAYBYTESTR] = 8, + [OARRAYLIT] = 8, + [OARRAYRUNESTR] = 8, + [OCALLFUNC] = 8, + [OCALLINTER] = 8, + [OCALLMETH] = 8, + [OCALL] = 8, + [OCAP] = 8, + [OCLOSE] = 8, + [OCONVIFACE] = 8, + [OCONVNOP] = 8, + [OCONV] = 8, + [OCOPY] = 8, + [ODELETE] = 8, + [OLEN] = 8, + [OLITERAL] = 8, + [OMAKESLICE] = 8, + [OMAKE] = 8, + [OMAPLIT] = 8, + [ONAME] = 8, + [ONEW] = 8, + [ONONAME] = 8, + [OPACK] = 8, + [OPANIC] = 8, + [OPAREN] = 8, + [OPRINTN] = 8, + [OPRINT] = 8, + [ORECV] = 8, + [ORUNESTR] = 8, + [OSTRARRAYBYTE] = 8, + [OSTRARRAYRUNE] = 8, + [OSTRUCTLIT] = 8, + [OTARRAY] = 8, + [OTCHAN] = 8, + [OTFUNC] = 8, + [OTINTER] = 8, + [OTMAP] = 8, + [OTPAREN] = 8, + [OTSTRUCT] = 8, + + [OINDEXMAP] = 8, + [OINDEX] = 8, + [OSLICE] = 8, + [OSLICESTR] = 8, + [OSLICEARR] = 8, + [ODOTINTER] = 8, + [ODOTMETH] = 8, + [ODOTPTR] = 8, + [ODOTTYPE2] = 8, + [ODOTTYPE] = 8, + [ODOT] = 8, + [OXDOT] = 8, + + [OPLUS] = 7, + [ONOT] = 7, + [OCOM] = 7, + [OMINUS] = 7, + [OADDR] = 7, + [OIND] = 7, + + [OMUL] = 6, + [ODIV] = 6, + [OMOD] = 6, + [OLSH] = 6, + [ORSH] = 6, + [OAND] = 6, + [OANDNOT] = 6, + + [OADD] = 5, + [OSUB] = 5, + [OOR] = 5, + [OXOR] = 5, + + [OEQ] = 4, + [OLT] = 4, + [OLE] = 4, + [OGE] = 4, + [OGT] = 4, + [ONE] = 4, + [OCMPSTR] = 4, + [OCMPIFACE] = 4, + + [OSEND] = 3, + [OANDAND] = 2, + [OOROR] = 1, + + // Statements handled by stmtfmt + [OAS] = -1, + [OAS2] = -1, + [OAS2DOTTYPE] = -1, + [OAS2FUNC] = -1, + [OAS2MAPR] = -1, + [OAS2RECV] = -1, + [OASOP] = -1, + [OBREAK] = -1, + [OCASE] = -1, + [OCONTINUE] = -1, + [ODCL] = -1, + [ODCLFIELD] = -1, + [ODEFER] = -1, + [OEMPTY] = -1, + [OFALL] = -1, + [OFOR] = -1, + [OIF] = -1, + [OLABEL] = -1, + [OPROC] = -1, + [ORANGE] = -1, + [ORETURN] = -1, + [OSELECT] = -1, + [OSWITCH] = -1, + [OXCASE] = -1, + [OXFALL] = -1, + + [OEND] = 0 +}; + +static int +exprfmt(Fmt *f, Node *n, int prec) +{ + int nprec; + NodeList *l; + Type *t; + + while(n && n->implicit && (n->op == OIND || n->op == OADDR)) + n = n->left; + + if(n == N) + return fmtstrcpy(f, "<N>"); + + nprec = opprec[n->op]; + if(n->op == OTYPE && n->sym != S) + nprec = 8; + + if(prec > nprec) + return fmtprint(f, "(%N)", n); + + switch(n->op) { + case OPAREN: + return fmtprint(f, "(%N)", n->left); + + case ODDDARG: + return fmtprint(f, "... argument"); + + case OREGISTER: + return fmtprint(f, "%R", n->val.u.reg); + + case OLITERAL: // this is a bit of a mess + if(fmtmode == FErr && n->sym != S) + return fmtprint(f, "%S", n->sym); + if(n->val.ctype == CTNIL) + n = n->orig; // if this node was a nil decorated with at type, print the original naked nil + if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { + // Need parens when type begins with what might + // be misinterpreted as a unary operator: * or <-. + if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv)) + return fmtprint(f, "(%T)(%V)", n->type, &n->val); + else + return fmtprint(f, "%T(%V)", n->type, &n->val); + } + return fmtprint(f, "%V", &n->val); + + case ONAME: + // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, + // but for export, this should be rendered as (*pkg.T).meth. + // These nodes have the special property that they are names with a left OTYPE and a right ONAME. + if(fmtmode == FExp && n->left && n->left->op == OTYPE && n->right && n->right->op == ONAME) { + if(isptr[n->left->type->etype]) + return fmtprint(f, "(%T).%hhS", n->left->type, n->right->sym); + else + return fmtprint(f, "%T.%hhS", n->left->type, n->right->sym); + } + //fallthrough + case OPACK: + case ONONAME: + return fmtprint(f, "%S", n->sym); + + case OTYPE: + if(n->type == T && n->sym != S) + return fmtprint(f, "%S", n->sym); + return fmtprint(f, "%T", n->type); + + case OTARRAY: + if(n->left) + return fmtprint(f, "[]%N", n->left); + return fmtprint(f, "[]%N", n->right); // happens before typecheck + + case OTPAREN: + return fmtprint(f, "(%N)", n->left); + + case OTMAP: + return fmtprint(f, "map[%N]%N", n->left, n->right); + + case OTCHAN: + switch(n->etype) { + case Crecv: + return fmtprint(f, "<-chan %N", n->left); + case Csend: + return fmtprint(f, "chan<- %N", n->left); + default: + if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv) + return fmtprint(f, "chan (%N)", n->left); + else + return fmtprint(f, "chan %N", n->left); + } + + case OTSTRUCT: + return fmtprint(f, "<struct>"); + + case OTINTER: + return fmtprint(f, "<inter>"); + + case OTFUNC: + return fmtprint(f, "<func>"); + + case OCLOSURE: + if(fmtmode == FErr) + return fmtstrcpy(f, "func literal"); + return fmtprint(f, "%T { %H }", n->type, n->nbody); + + case OCOMPLIT: + if(fmtmode == FErr) + return fmtstrcpy(f, "composite literal"); + return fmtprint(f, "%N{ %,H }", n->right, n->list); + + case OPTRLIT: + if(fmtmode == FExp && n->left->implicit) + return fmtprint(f, "%N", n->left); + return fmtprint(f, "&%N", n->left); + + case OSTRUCTLIT: + if(fmtmode == FExp) { // requires special handling of field names + if(n->implicit) + fmtstrcpy(f, "{"); + else + fmtprint(f, "%T{", n->type); + for(l=n->list; l; l=l->next) { + // another special case: if n->left is an embedded field of builtin type, + // it needs to be non-qualified. Can't figure that out in %S, so do it here + if(l->n->left->type->embedded) { + t = l->n->left->type->type; + if(t->sym == S) + t = t->type; + fmtprint(f, " %T:%N", t, l->n->right); + } else + fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right); + + if(l->next) + fmtstrcpy(f, ","); + else + fmtstrcpy(f, " "); + } + return fmtstrcpy(f, "}"); + } + // fallthrough + + case OARRAYLIT: + case OMAPLIT: + if(fmtmode == FErr) + return fmtprint(f, "%T literal", n->type); + if(fmtmode == FExp && n->implicit) + return fmtprint(f, "{ %,H }", n->list); + return fmtprint(f, "%T{ %,H }", n->type, n->list); + + case OKEY: + if(n->left && n->right) + return fmtprint(f, "%N:%N", n->left, n->right); + if(!n->left && n->right) + return fmtprint(f, ":%N", n->right); + if(n->left && !n->right) + return fmtprint(f, "%N:", n->left); + return fmtstrcpy(f, ":"); + + case OXDOT: + case ODOT: + case ODOTPTR: + case ODOTINTER: + case ODOTMETH: + exprfmt(f, n->left, nprec); + if(n->right == N || n->right->sym == S) + fmtstrcpy(f, ".<nil>"); + return fmtprint(f, ".%hhS", n->right->sym); + + case ODOTTYPE: + case ODOTTYPE2: + exprfmt(f, n->left, nprec); + if(n->right != N) + return fmtprint(f, ".(%N)", n->right); + return fmtprint(f, ".(%T)", n->type); + + case OINDEX: + case OINDEXMAP: + case OSLICE: + case OSLICESTR: + case OSLICEARR: + exprfmt(f, n->left, nprec); + return fmtprint(f, "[%N]", n->right); + + case OCOPY: + case OCOMPLEX: + return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right); + + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case ORUNESTR: + if(n->type == T || n->type->sym == S) + return fmtprint(f, "(%T)(%N)", n->type, n->left); + if(n->left) + return fmtprint(f, "%T(%N)", n->type, n->left); + return fmtprint(f, "%T(%,H)", n->type, n->list); + + case OREAL: + case OIMAG: + case OAPPEND: + case OCAP: + case OCLOSE: + case ODELETE: + case OLEN: + case OMAKE: + case ONEW: + case OPANIC: + case OPRINT: + case OPRINTN: + if(n->left) + return fmtprint(f, "%#O(%N)", n->op, n->left); + if(n->isddd) + return fmtprint(f, "%#O(%,H...)", n->op, n->list); + return fmtprint(f, "%#O(%,H)", n->op, n->list); + + case OCALL: + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + exprfmt(f, n->left, nprec); + if(n->isddd) + return fmtprint(f, "(%,H...)", n->list); + return fmtprint(f, "(%,H)", n->list); + + case OMAKEMAP: + case OMAKECHAN: + case OMAKESLICE: + if(n->list) // pre-typecheck + return fmtprint(f, "make(%T, %,H)", n->type, n->list); + if(n->right) + return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right); + if(n->left) + return fmtprint(f, "make(%T, %N)", n->type, n->left); + return fmtprint(f, "make(%T)", n->type); + + // Unary + case OPLUS: + case OMINUS: + case OADDR: + case OCOM: + case OIND: + case ONOT: + case ORECV: + if(n->left->op == n->op) + fmtprint(f, "%#O ", n->op); + else + fmtprint(f, "%#O", n->op); + return exprfmt(f, n->left, nprec+1); + + // Binary + case OADD: + case OADDSTR: + case OAND: + case OANDAND: + case OANDNOT: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLT: + case OLSH: + case OMOD: + case OMUL: + case ONE: + case OOR: + case OOROR: + case ORSH: + case OSEND: + case OSUB: + case OXOR: + exprfmt(f, n->left, nprec); + fmtprint(f, " %#O ", n->op); + exprfmt(f, n->right, nprec+1); + return 0; + + case OCMPSTR: + case OCMPIFACE: + exprfmt(f, n->left, nprec); + fmtprint(f, " %#O ", n->etype); + exprfmt(f, n->right, nprec+1); + return 0; + } + + return fmtprint(f, "<node %O>", n->op); +} + +static int +nodefmt(Fmt *f, Node *n) +{ + Type *t; + + t = n->type; + + // we almost always want the original, except in export mode for literals + // this saves the importer some work, and avoids us having to redo some + // special casing for package unsafe + if((fmtmode != FExp || n->op != OLITERAL) && n->orig != N) + n = n->orig; + + if(f->flags&FmtLong && t != T) { + if(t->etype == TNIL) + return fmtprint(f, "nil"); + else + return fmtprint(f, "%N (type %T)", n, t); + } + + // TODO inlining produces expressions with ninits. we can't print these yet. + + if(opprec[n->op] < 0) + return stmtfmt(f, n); + + return exprfmt(f, n, 0); +} + +static int dumpdepth; + +static void +indent(Fmt *fp) +{ + int i; + + fmtstrcpy(fp, "\n"); + for(i = 0; i < dumpdepth; ++i) + fmtstrcpy(fp, ". "); +} + +static int +nodedump(Fmt *fp, Node *n) +{ + int recur; + + if(n == N) + return 0; + + recur = !(fp->flags&FmtShort); + + if(recur) { + indent(fp); + if(dumpdepth > 10) + return fmtstrcpy(fp, "..."); + + if(n->ninit != nil) { + fmtprint(fp, "%O-init%H", n->op, n->ninit); + indent(fp); + } + } + +// fmtprint(fp, "[%p]", n); + + switch(n->op) { + default: + fmtprint(fp, "%O%J", n->op, n); + break; + case OREGISTER: + fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n); + break; + case OLITERAL: + fmtprint(fp, "%O-%V%J", n->op, &n->val, n); + break; + case ONAME: + case ONONAME: + if(n->sym != S) + fmtprint(fp, "%O-%S%J", n->op, n->sym, n); + else + fmtprint(fp, "%O%J", n->op, n); + break; + case OASOP: + fmtprint(fp, "%O-%O%J", n->op, n->etype, n); + break; + case OTYPE: + fmtprint(fp, "%O %S%J type=%T", n->op, n->sym, n, n->type); + if(recur && n->type == T && n->ntype) { + indent(fp); + fmtprint(fp, "%O-ntype%N", n->op, n->ntype); + } + break; + } + + if(n->sym != S && n->op != ONAME) + fmtprint(fp, " %S G%d", n->sym, n->vargen); + + if(n->type != T) + fmtprint(fp, " %T", n->type); + + if(recur) { + if(n->left) + fmtprint(fp, "%N", n->left); + if(n->right) + fmtprint(fp, "%N", n->right); + if(n->list) { + indent(fp); + fmtprint(fp, "%O-list%H", n->op, n->list); + } + if(n->rlist) { + indent(fp); + fmtprint(fp, "%O-rlist%H", n->op, n->rlist); + } + if(n->ntest) { + indent(fp); + fmtprint(fp, "%O-test%N", n->op, n->ntest); + } + if(n->nbody) { + indent(fp); + fmtprint(fp, "%O-body%H", n->op, n->nbody); + } + if(n->nelse) { + indent(fp); + fmtprint(fp, "%O-else%H", n->op, n->nelse); + } + if(n->nincr) { + indent(fp); + fmtprint(fp, "%O-incr%N", n->op, n->nincr); + } + } + + return 0; +} + +// Fmt "%S": syms +// Flags: "%hS" suppresses qualifying with package +static int +Sconv(Fmt *fp) +{ + Sym *s; + int r, sm; + unsigned long sf; + + s = va_arg(fp->args, Sym*); + if(s == S) + return fmtstrcpy(fp, "<S>"); + + if(s->name && s->name[0] == '_' && s->name[1] == '\0') + return fmtstrcpy(fp, "_"); + + sf = fp->flags; + sm = setfmode(&fp->flags); + r = symfmt(fp, s); + fp->flags = sf; + fmtmode = sm; + return r; +} + +// Fmt "%T": types. +// Flags: 'l' print definition, not name +// 'h' omit 'func' and receiver from function types, short type names +// 'u' package name, not prefix (FTypeId mode, sticky) +static int +Tconv(Fmt *fp) +{ + Type *t; + int r, sm; + unsigned long sf; + + t = va_arg(fp->args, Type*); + if(t == T) + return fmtstrcpy(fp, "<T>"); + + if(t->trecur > 4) + return fmtstrcpy(fp, "<...>"); + + t->trecur++; + sf = fp->flags; + sm = setfmode(&fp->flags); + + if(fmtmode == FTypeId && (sf&FmtUnsigned)) + fmtpkgpfx++; + if(fmtpkgpfx) + fp->flags |= FmtUnsigned; + + r = typefmt(fp, t); + + if(fmtmode == FTypeId && (sf&FmtUnsigned)) + fmtpkgpfx--; + + fp->flags = sf; + fmtmode = sm; + t->trecur--; + return r; +} + +// Fmt '%N': Nodes. +// Flags: 'l' suffix with "(type %T)" where possible +// '+h' in debug mode, don't recurse, no multiline output +static int +Nconv(Fmt *fp) +{ + Node *n; + int r, sm; + unsigned long sf; + + n = va_arg(fp->args, Node*); + if(n == N) + return fmtstrcpy(fp, "<N>"); + sf = fp->flags; + sm = setfmode(&fp->flags); + + r = -1; + switch(fmtmode) { + case FErr: + case FExp: + r = nodefmt(fp, n); + break; + case FDbg: + dumpdepth++; + r = nodedump(fp, n); + dumpdepth--; + break; + default: + fatal("unhandled %%N mode"); + } + + fp->flags = sf; + fmtmode = sm; + return r; +} + +// Fmt '%H': NodeList. +// Flags: all those of %N plus ',': separate with comma's instead of semicolons. +static int +Hconv(Fmt *fp) +{ + NodeList *l; + int r, sm; + unsigned long sf; + char *sep; + + l = va_arg(fp->args, NodeList*); + + if(l == nil && fmtmode == FDbg) + return fmtstrcpy(fp, "<nil>"); + + sf = fp->flags; + sm = setfmode(&fp->flags); + r = 0; + sep = "; "; + if(fmtmode == FDbg) + sep = "\n"; + else if(fp->flags & FmtComma) + sep = ", "; + + for(;l; l=l->next) { + r += fmtprint(fp, "%N", l->n); + if(l->next) + r += fmtstrcpy(fp, sep); + } + + fp->flags = sf; + fmtmode = sm; + return r; +} + +void +fmtinstallgo(void) +{ + fmtmode = FErr; + fmtinstall('E', Econv); // etype opcodes + fmtinstall('J', Jconv); // all the node flags + fmtinstall('H', Hconv); // node lists + fmtinstall('L', Lconv); // line number + fmtinstall('N', Nconv); // node pointer + fmtinstall('O', Oconv); // node opcodes + fmtinstall('S', Sconv); // sym pointer + fmtinstall('T', Tconv); // type pointer + fmtinstall('V', Vconv); // val pointer + fmtinstall('Z', Zconv); // escaped string + + // These are in mparith1.c + fmtinstall('B', Bconv); // big numbers + fmtinstall('F', Fconv); // big float numbers + +} + +void +dumplist(char *s, NodeList *l) +{ + print("%s\n%+H\n", s, l); +} + +void +dump(char *s, Node *n) +{ + print("%s [%p]\n%+N\n", s, n, n); +} |