mirror of
https://github.com/golang/go
synced 2024-10-04 20:11:22 -06:00
25922c0658
The original implementation of closures created the underlying top-level function during walk, which is fairly late in the compilation process and caused ordering-based complications due to earlier stages that had to be repeated any number of times. Create the underlying function during typecheck, much earlier, so that later stages can be run just once. The result is a simpler compilation sequence. R=ken2 CC=golang-dev https://golang.org/cl/6279049
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,
|
|
[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");
|
|
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, " %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);
|
|
}
|