| // 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); |
| } |