| // 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" strip type qualifier off of method name | |
| // | |
| // %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']) | |
| 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] = "--", | |
| [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(!c && n->used != 0) | |
| fmtprint(fp, " used(%d)", n->used); | |
| return 0; | |
| } | |
| // Fmt "%V": Values | |
| static int | |
| Vconv(Fmt *fp) | |
| { | |
| Val *v; | |
| v = va_arg(fp->args, Val*); | |
| switch(v->ctype) { | |
| case CTINT: | |
| return fmtprint(fp, "%B", v->u.xval); | |
| case CTFLT: | |
| return fmtprint(fp, "%F", v->u.fval); | |
| case CTCPLX: // ? 1234i -> (0p+0+617p+1) | |
| return fmtprint(fp, "(%F+%F)", &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(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: | |
| return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name); | |
| } | |
| } | |
| if(fp->flags&FmtByte) { | |
| // skip leading "type." in method name | |
| p = utfrrune(s->name, '.'); | |
| if(p) | |
| return fmtstrcpy(fp, p+1); | |
| } | |
| 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; | |
| 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: | |
| 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) { // 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, " %T;", t1); | |
| else | |
| fmtprint(fp, " %T ", t1); | |
| fmtstrcpy(fp, "}"); | |
| } | |
| return 0; | |
| case TFIELD: | |
| if(!(fp->flags&FmtShort)) { | |
| if(t->sym != S && !t->embedded) | |
| fmtprint(fp, "%hS ", t->sym); | |
| if((!t->sym || t->embedded) && fmtmode == FExp) | |
| 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: | |
| switch(n->left->class) { | |
| case PFUNC: | |
| case PEXTERN: | |
| fmtprint(f, "var %S %T", n->left->sym, n->left->type); | |
| break; | |
| default: | |
| fmtprint(f, "var %hS %T", n->left->sym, n->left->type); | |
| break; | |
| } | |
| 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 OAS2MAPW: | |
| 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; | |
| } | |
| if(extrablock) | |
| fmtstrcpy(f, "}"); | |
| return 0; | |
| } | |
| static int opprec[] = { | |
| [OAPPEND] = 8, | |
| [OARRAYBYTESTR] = 8, | |
| [OCALLFUNC] = 8, | |
| [OCALLINTER] = 8, | |
| [OCALLMETH] = 8, | |
| [OCALL] = 8, | |
| [OCAP] = 8, | |
| [OCLOSE] = 8, | |
| [OCONVIFACE] = 8, | |
| [OCONVNOP] = 8, | |
| [OCONV] = 8, | |
| [OCOPY] = 8, | |
| [OLEN] = 8, | |
| [OLITERAL] = 8, | |
| [OMAKESLICE] = 8, | |
| [OMAKE] = 8, | |
| [ONAME] = 8, | |
| [ONEW] = 8, | |
| [ONONAME] = 8, | |
| [OPACK] = 8, | |
| [OPANIC] = 8, | |
| [OPAREN] = 8, | |
| [OPRINTN] = 8, | |
| [OPRINT] = 8, | |
| [ORECV] = 8, | |
| [ORUNESTR] = 8, | |
| [OTPAREN] = 8, | |
| [OINDEXMAP] = 8, | |
| [OINDEX] = 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, | |
| [OSEND] = 3, | |
| [OANDAND] = 2, | |
| [OOROR] = 1, | |
| // Statements handled by stmtfmt | |
| [OAS] = -1, | |
| [OAS2] = -1, | |
| [OAS2DOTTYPE] = -1, | |
| [OAS2FUNC] = -1, | |
| [OAS2MAPR] = -1, | |
| [OAS2MAPW] = -1, | |
| [OAS2RECV] = -1, | |
| [OASOP] = -1, | |
| [OBREAK] = -1, | |
| [OCASE] = -1, | |
| [OCONTINUE] = -1, | |
| [ODCL] = -1, | |
| [ODCLFIELD] = -1, | |
| [ODEFER] = -1, | |
| [OFALL] = -1, | |
| [OFOR] = -1, | |
| [OIF] = -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; | |
| while(n && n->implicit) | |
| 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 still a bit of a mess | |
| if(fmtmode == FErr && n->sym != S) | |
| return fmtprint(f, "%S", n->sym); | |
| if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { | |
| if(isptr[n->type->etype]) | |
| 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: | |
| case OPACK: | |
| case ONONAME: | |
| if(fmtmode == FExp) { | |
| switch(n->class&~PHEAP) { | |
| case PEXTERN: | |
| case PFUNC: | |
| break; | |
| default: | |
| return fmtprint(f, "%hS", n->sym); | |
| } | |
| } | |
| 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: | |
| return fmtprint(f, "[]%N", n->left); | |
| 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 OPLUS: | |
| case OMINUS: | |
| if(n->left->op == n->op) | |
| return fmtprint(f, "%#O %N", n->op, n->left); | |
| // fallthrough | |
| case OADDR: | |
| case OCOM: | |
| case OIND: | |
| case ONOT: | |
| case ORECV: | |
| return fmtprint(f, "%#O%N", n->op, n->left); | |
| case OCLOSURE: | |
| if(fmtmode == FErr) | |
| return fmtstrcpy(f, "func literal"); | |
| // return fmtprint(f, "%T { %H }", n->type, n->nbody); this prints the list/rlist turned to types, not what we want | |
| if(!n->rlist) | |
| return fmtprint(f, "func(%,H) { %H } ", n->list, n->nbody); | |
| if(!n->rlist->next && !n->rlist->n->left) | |
| return fmtprint(f, "func(%,H) %N { %H } ", n->list, n->rlist->n->right, n->nbody); | |
| return fmtprint(f, "func(%,H) (%,H) { %H } ", n->list, n->rlist, n->nbody); | |
| case OCOMPLIT: | |
| return fmtstrcpy(f, "composite literal"); | |
| case OARRAYLIT: | |
| case OMAPLIT: | |
| case OSTRUCTLIT: | |
| if(fmtmode == FErr) | |
| return fmtprint(f, "%T literal", n->type); | |
| 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 OSTRARRAYBYTE: | |
| 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 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->next) | |
| return fmtprint(f, "make(%T, %,H)", n->type, n->list->next); | |
| return fmtprint(f, "make(%T)", n->type); | |
| 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: | |
| 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) | |
| { | |
| if(f->flags&FmtLong && n->type != T) { | |
| if(n->type->etype == TNIL) | |
| return fmtprint(f, "nil"); | |
| else | |
| return fmtprint(f, "%N (type %T)", n, n->type); | |
| } | |
| // 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; | |
| 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, "...\n"); | |
| if(n->ninit != nil) { | |
| fmtprint(fp, "%O-init\n%H", n->op, n->ninit); | |
| indent(fp); | |
| } | |
| } | |
| 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 type=%T", n->op, n->sym, n->type); | |
| if(recur && n->type == T && n->ntype) { | |
| fmtstrcpy(fp, "\n"); | |
| indent(fp); | |
| fmtprint(fp, "%O-ntype\n%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) { | |
| fmtstrcpy(fp, "\n"); | |
| 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\n%H", n->op, n->list); | |
| } | |
| if(n->rlist) { | |
| indent(fp); | |
| fmtprint(fp, "%O-rlist\n%H", n->op, n->rlist); | |
| } | |
| if(n->ntest) { | |
| indent(fp); | |
| fmtprint(fp, "%O-test\n%N", n->op, n->ntest); | |
| } | |
| if(n->nbody) { | |
| indent(fp); | |
| fmtprint(fp, "%O-body\n%H", n->op, n->nbody); | |
| } | |
| if(n->nelse) { | |
| indent(fp); | |
| fmtprint(fp, "%O-else\n%H", n->op, n->nelse); | |
| } | |
| if(n->nincr) { | |
| indent(fp); | |
| fmtprint(fp, "%O-incr\n%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>"); | |
| 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: | |
| if(n->orig != N) | |
| n = n->orig; | |
| 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", s, l); | |
| } | |
| void | |
| dump(char *s, Node *n) | |
| { | |
| print("%s [%p]\n%+N", s, n, n); | |
| } |