mirror of
https://github.com/golang/go
synced 2024-11-20 10:14:43 -07:00
54191126e4
R=ken2 CC=golang-dev https://golang.org/cl/6609052
1644 lines
34 KiB
C
1644 lines
34 KiB
C
// 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:
|
|
if((fp->flags & FmtSharp) || fmtmode == FExp)
|
|
return fmtprint(fp, "%#B", v->u.xval);
|
|
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,
|
|
[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,
|
|
[ORECV] = 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->orig != N)
|
|
n = n->orig; // if this node was a nil decorated with at type, print the original naked nil
|
|
if(n->type != T && 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");
|
|
if(n->nbody)
|
|
return fmtprint(f, "%T { %H }", n->type, n->nbody);
|
|
return fmtprint(f, "%T { %H }", n->type, n->closure->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, " %hhS:%N", t->sym, 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);
|
|
}
|