blob: a3afbe50796a02caf14bc26dd11e33dcc7dd7537 [file] [log] [blame]
/**
* Generate $(LINK2 https://dlang.org/dmd-windows.html#interface-files, D interface files).
*
* Also used to convert AST nodes to D code in general, e.g. for error messages or `printf` debugging.
*
* Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d, _hdrgen.d)
* Documentation: https://dlang.org/phobos/dmd_hdrgen.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/hdrgen.d
*/
module dmd.hdrgen;
import core.stdc.ctype;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.arraytypes;
import dmd.astenums;
import dmd.attrib;
import dmd.cond;
import dmd.ctfeexpr;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.dimport;
import dmd.dmodule;
import dmd.doc;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.dversion;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.nspace;
import dmd.parse;
import dmd.root.complex;
import dmd.root.ctfloat;
import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.statement;
import dmd.staticassert;
import dmd.target;
import dmd.tokens;
import dmd.utils;
import dmd.visitor;
struct HdrGenState
{
bool hdrgen; /// true if generating header file
bool ddoc; /// true if generating Ddoc file
bool fullDump; /// true if generating a full AST dump file
bool fullQual; /// fully qualify types when printing
int tpltMember;
int autoMember;
int forStmtInit;
bool declstring; // set while declaring alias for string,wstring or dstring
EnumDeclaration inEnumDecl;
}
enum TEST_EMIT_ALL = 0;
extern (C++) void genhdrfile(Module m)
{
OutBuffer buf;
buf.doindent = 1;
buf.printf("// D import file generated from '%s'", m.srcfile.toChars());
buf.writenl();
HdrGenState hgs;
hgs.hdrgen = true;
toCBuffer(m, &buf, &hgs);
writeFile(m.loc, m.hdrfile.toString(), buf[]);
}
/**
* Dumps the full contents of module `m` to `buf`.
* Params:
* buf = buffer to write to.
* m = module to visit all members of.
*/
extern (C++) void moduleToBuffer(OutBuffer* buf, Module m)
{
HdrGenState hgs;
hgs.fullDump = true;
toCBuffer(m, buf, &hgs);
}
void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs)
{
if (m.md)
{
if (m.userAttribDecl)
{
buf.writestring("@(");
argsToBuffer(m.userAttribDecl.atts, buf, hgs);
buf.writeByte(')');
buf.writenl();
}
if (m.md.isdeprecated)
{
if (m.md.msg)
{
buf.writestring("deprecated(");
m.md.msg.expressionToBuffer(buf, hgs);
buf.writestring(") ");
}
else
buf.writestring("deprecated ");
}
buf.writestring("module ");
buf.writestring(m.md.toChars());
buf.writeByte(';');
buf.writenl();
}
foreach (s; *m.members)
{
s.dsymbolToBuffer(buf, hgs);
}
}
private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new StatementPrettyPrintVisitor(buf, hgs);
s.accept(v);
}
private extern (C++) final class StatementPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
override void visit(Statement s)
{
buf.writestring("Statement::toCBuffer()");
buf.writenl();
assert(0);
}
override void visit(ErrorStatement s)
{
buf.writestring("__error__");
buf.writenl();
}
override void visit(ExpStatement s)
{
if (s.exp && s.exp.op == EXP.declaration &&
(cast(DeclarationExp)s.exp).declaration)
{
// bypass visit(DeclarationExp)
(cast(DeclarationExp)s.exp).declaration.dsymbolToBuffer(buf, hgs);
return;
}
if (s.exp)
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(';');
if (!hgs.forStmtInit)
buf.writenl();
}
override void visit(CompileStatement s)
{
buf.writestring("mixin(");
argsToBuffer(s.exps, buf, hgs, null);
buf.writestring(");");
if (!hgs.forStmtInit)
buf.writenl();
}
override void visit(CompoundStatement s)
{
foreach (sx; *s.statements)
{
if (sx)
sx.accept(this);
}
}
override void visit(CompoundDeclarationStatement s)
{
bool anywritten = false;
foreach (sx; *s.statements)
{
auto ds = sx ? sx.isExpStatement() : null;
if (ds && ds.exp.op == EXP.declaration)
{
auto d = (cast(DeclarationExp)ds.exp).declaration;
assert(d.isDeclaration());
if (auto v = d.isVarDeclaration())
{
scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs);
ppv.visitVarDecl(v, anywritten);
}
else
d.dsymbolToBuffer(buf, hgs);
anywritten = true;
}
}
buf.writeByte(';');
if (!hgs.forStmtInit)
buf.writenl();
}
override void visit(UnrolledLoopStatement s)
{
buf.writestring("/*unrolled*/ {");
buf.writenl();
buf.level++;
foreach (sx; *s.statements)
{
if (sx)
sx.accept(this);
}
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(ScopeStatement s)
{
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s.statement)
s.statement.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(WhileStatement s)
{
buf.writestring("while (");
if (auto p = s.param)
{
// Print condition assignment
StorageClass stc = p.storageClass;
if (!p.type && !stc)
stc = STC.auto_;
if (stcToBuffer(buf, stc))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
buf.writestring(" = ");
}
s.condition.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
if (s._body)
s._body.accept(this);
}
override void visit(DoStatement s)
{
buf.writestring("do");
buf.writenl();
if (s._body)
s._body.accept(this);
buf.writestring("while (");
s.condition.expressionToBuffer(buf, hgs);
buf.writestring(");");
buf.writenl();
}
override void visit(ForStatement s)
{
buf.writestring("for (");
if (s._init)
{
hgs.forStmtInit++;
s._init.accept(this);
hgs.forStmtInit--;
}
else
buf.writeByte(';');
if (s.condition)
{
buf.writeByte(' ');
s.condition.expressionToBuffer(buf, hgs);
}
buf.writeByte(';');
if (s.increment)
{
buf.writeByte(' ');
s.increment.expressionToBuffer(buf, hgs);
}
buf.writeByte(')');
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
private void foreachWithoutBody(ForeachStatement s)
{
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
foreach (i, p; *s.parameters)
{
if (i)
buf.writestring(", ");
if (stcToBuffer(buf, p.storageClass))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
}
buf.writestring("; ");
s.aggr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
override void visit(ForeachStatement s)
{
foreachWithoutBody(s);
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
private void foreachRangeWithoutBody(ForeachRangeStatement s)
{
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
if (s.prm.type)
typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
else
buf.writestring(s.prm.ident.toString());
buf.writestring("; ");
s.lwr.expressionToBuffer(buf, hgs);
buf.writestring(" .. ");
s.upr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
override void visit(ForeachRangeStatement s)
{
foreachRangeWithoutBody(s);
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(StaticForeachStatement s)
{
buf.writestring("static ");
if (s.sfe.aggrfe)
{
visit(s.sfe.aggrfe);
}
else
{
assert(s.sfe.rangefe);
visit(s.sfe.rangefe);
}
}
override void visit(ForwardingStatement s)
{
s.statement.accept(this);
}
override void visit(IfStatement s)
{
buf.writestring("if (");
if (Parameter p = s.prm)
{
StorageClass stc = p.storageClass;
if (!p.type && !stc)
stc = STC.auto_;
if (stcToBuffer(buf, stc))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
buf.writestring(" = ");
}
s.condition.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
if (s.ifbody.isScopeStatement())
{
s.ifbody.accept(this);
}
else
{
buf.level++;
s.ifbody.accept(this);
buf.level--;
}
if (s.elsebody)
{
buf.writestring("else");
if (!s.elsebody.isIfStatement())
{
buf.writenl();
}
else
{
buf.writeByte(' ');
}
if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
{
s.elsebody.accept(this);
}
else
{
buf.level++;
s.elsebody.accept(this);
buf.level--;
}
}
}
override void visit(ConditionalStatement s)
{
s.condition.conditionToBuffer(buf, hgs);
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s.ifbody)
s.ifbody.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
if (s.elsebody)
{
buf.writestring("else");
buf.writenl();
buf.writeByte('{');
buf.level++;
buf.writenl();
s.elsebody.accept(this);
buf.level--;
buf.writeByte('}');
}
buf.writenl();
}
override void visit(PragmaStatement s)
{
buf.writestring("pragma (");
buf.writestring(s.ident.toString());
if (s.args && s.args.dim)
{
buf.writestring(", ");
argsToBuffer(s.args, buf, hgs);
}
buf.writeByte(')');
if (s._body)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
else
{
buf.writeByte(';');
buf.writenl();
}
}
override void visit(StaticAssertStatement s)
{
s.sa.dsymbolToBuffer(buf, hgs);
}
override void visit(SwitchStatement s)
{
buf.writestring(s.isFinal ? "final switch (" : "switch (");
s.condition.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
if (s._body)
{
if (!s._body.isScopeStatement())
{
buf.writeByte('{');
buf.writenl();
buf.level++;
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
else
{
s._body.accept(this);
}
}
}
override void visit(CaseStatement s)
{
buf.writestring("case ");
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(':');
buf.writenl();
s.statement.accept(this);
}
override void visit(CaseRangeStatement s)
{
buf.writestring("case ");
s.first.expressionToBuffer(buf, hgs);
buf.writestring(": .. case ");
s.last.expressionToBuffer(buf, hgs);
buf.writeByte(':');
buf.writenl();
s.statement.accept(this);
}
override void visit(DefaultStatement s)
{
buf.writestring("default:");
buf.writenl();
s.statement.accept(this);
}
override void visit(GotoDefaultStatement s)
{
buf.writestring("goto default;");
buf.writenl();
}
override void visit(GotoCaseStatement s)
{
buf.writestring("goto case");
if (s.exp)
{
buf.writeByte(' ');
s.exp.expressionToBuffer(buf, hgs);
}
buf.writeByte(';');
buf.writenl();
}
override void visit(SwitchErrorStatement s)
{
buf.writestring("SwitchErrorStatement::toCBuffer()");
buf.writenl();
}
override void visit(ReturnStatement s)
{
buf.writestring("return ");
if (s.exp)
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(BreakStatement s)
{
buf.writestring("break");
if (s.ident)
{
buf.writeByte(' ');
buf.writestring(s.ident.toString());
}
buf.writeByte(';');
buf.writenl();
}
override void visit(ContinueStatement s)
{
buf.writestring("continue");
if (s.ident)
{
buf.writeByte(' ');
buf.writestring(s.ident.toString());
}
buf.writeByte(';');
buf.writenl();
}
override void visit(SynchronizedStatement s)
{
buf.writestring("synchronized");
if (s.exp)
{
buf.writeByte('(');
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
if (s._body)
{
buf.writeByte(' ');
s._body.accept(this);
}
}
override void visit(WithStatement s)
{
buf.writestring("with (");
s.exp.expressionToBuffer(buf, hgs);
buf.writestring(")");
buf.writenl();
if (s._body)
s._body.accept(this);
}
override void visit(TryCatchStatement s)
{
buf.writestring("try");
buf.writenl();
if (s._body)
{
if (s._body.isScopeStatement())
{
s._body.accept(this);
}
else
{
buf.level++;
s._body.accept(this);
buf.level--;
}
}
foreach (c; *s.catches)
{
visit(c);
}
}
override void visit(TryFinallyStatement s)
{
buf.writestring("try");
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
buf.writestring("finally");
buf.writenl();
if (s.finalbody.isScopeStatement())
{
s.finalbody.accept(this);
}
else
{
buf.level++;
s.finalbody.accept(this);
buf.level--;
}
}
override void visit(ScopeGuardStatement s)
{
buf.writestring(Token.toString(s.tok));
buf.writeByte(' ');
if (s.statement)
s.statement.accept(this);
}
override void visit(ThrowStatement s)
{
buf.writestring("throw ");
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(DebugStatement s)
{
if (s.statement)
{
s.statement.accept(this);
}
}
override void visit(GotoStatement s)
{
buf.writestring("goto ");
buf.writestring(s.ident.toString());
buf.writeByte(';');
buf.writenl();
}
override void visit(LabelStatement s)
{
buf.writestring(s.ident.toString());
buf.writeByte(':');
buf.writenl();
if (s.statement)
s.statement.accept(this);
}
override void visit(AsmStatement s)
{
buf.writestring("asm { ");
Token* t = s.tokens;
buf.level++;
while (t)
{
buf.writestring(t.toChars());
if (t.next &&
t.value != TOK.min &&
t.value != TOK.comma && t.next.value != TOK.comma &&
t.value != TOK.leftBracket && t.next.value != TOK.leftBracket &&
t.next.value != TOK.rightBracket &&
t.value != TOK.leftParenthesis && t.next.value != TOK.leftParenthesis &&
t.next.value != TOK.rightParenthesis &&
t.value != TOK.dot && t.next.value != TOK.dot)
{
buf.writeByte(' ');
}
t = t.next;
}
buf.level--;
buf.writestring("; }");
buf.writenl();
}
override void visit(ImportStatement s)
{
foreach (imp; *s.imports)
{
imp.dsymbolToBuffer(buf, hgs);
}
}
void visit(Catch c)
{
buf.writestring("catch");
if (c.type)
{
buf.writeByte('(');
typeToBuffer(c.type, c.ident, buf, hgs);
buf.writeByte(')');
}
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (c.handler)
c.handler.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
}
private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
s.accept(v);
}
private extern (C++) final class DsymbolPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
////////////////////////////////////////////////////////////////////////////
override void visit(Dsymbol s)
{
buf.writestring(s.toChars());
}
override void visit(StaticAssert s)
{
buf.writestring(s.kind());
buf.writeByte('(');
s.exp.expressionToBuffer(buf, hgs);
if (s.msg)
{
buf.writestring(", ");
s.msg.expressionToBuffer(buf, hgs);
}
buf.writestring(");");
buf.writenl();
}
override void visit(DebugSymbol s)
{
buf.writestring("debug = ");
if (s.ident)
buf.writestring(s.ident.toString());
else
buf.print(s.level);
buf.writeByte(';');
buf.writenl();
}
override void visit(VersionSymbol s)
{
buf.writestring("version = ");
if (s.ident)
buf.writestring(s.ident.toString());
else
buf.print(s.level);
buf.writeByte(';');
buf.writenl();
}
override void visit(EnumMember em)
{
if (em.type)
typeToBuffer(em.type, em.ident, buf, hgs);
else
buf.writestring(em.ident.toString());
if (em.value)
{
buf.writestring(" = ");
em.value.expressionToBuffer(buf, hgs);
}
}
override void visit(Import imp)
{
if (hgs.hdrgen && imp.id == Id.object)
return; // object is imported by default
if (imp.isstatic)
buf.writestring("static ");
buf.writestring("import ");
if (imp.aliasId)
{
buf.printf("%s = ", imp.aliasId.toChars());
}
foreach (const pid; imp.packages)
{
buf.printf("%s.", pid.toChars());
}
buf.writestring(imp.id.toString());
if (imp.names.dim)
{
buf.writestring(" : ");
foreach (const i, const name; imp.names)
{
if (i)
buf.writestring(", ");
const _alias = imp.aliases[i];
if (_alias)
buf.printf("%s = %s", _alias.toChars(), name.toChars());
else
buf.writestring(name.toChars());
}
}
buf.writeByte(';');
buf.writenl();
}
override void visit(AliasThis d)
{
buf.writestring("alias ");
buf.writestring(d.ident.toString());
buf.writestring(" this;\n");
}
override void visit(AttribDeclaration d)
{
bool hasSTC;
if (auto stcd = d.isStorageClassDeclaration)
{
hasSTC = stcToBuffer(buf, stcd.stc);
}
if (!d.decl)
{
buf.writeByte(';');
buf.writenl();
return;
}
if (d.decl.dim == 0 || (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration()))
{
// hack for bugzilla 8081
if (hasSTC) buf.writeByte(' ');
buf.writestring("{}");
}
else if (d.decl.dim == 1)
{
if (hasSTC) buf.writeByte(' ');
(*d.decl)[0].accept(this);
return;
}
else
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (de; *d.decl)
de.accept(this);
buf.level--;
buf.writeByte('}');
}
buf.writenl();
}
override void visit(StorageClassDeclaration d)
{
visit(cast(AttribDeclaration)d);
}
override void visit(DeprecatedDeclaration d)
{
buf.writestring("deprecated(");
d.msg.expressionToBuffer(buf, hgs);
buf.writestring(") ");
visit(cast(AttribDeclaration)d);
}
override void visit(LinkDeclaration d)
{
buf.writestring("extern (");
buf.writestring(linkageToString(d.linkage));
buf.writestring(") ");
visit(cast(AttribDeclaration)d);
}
override void visit(CPPMangleDeclaration d)
{
string s;
final switch (d.cppmangle)
{
case CPPMANGLE.asClass:
s = "class";
break;
case CPPMANGLE.asStruct:
s = "struct";
break;
case CPPMANGLE.def:
break;
}
buf.writestring("extern (C++, ");
buf.writestring(s);
buf.writestring(") ");
visit(cast(AttribDeclaration)d);
}
override void visit(VisibilityDeclaration d)
{
visibilityToBuffer(buf, d.visibility);
AttribDeclaration ad = cast(AttribDeclaration)d;
if (ad.decl.dim <= 1)
buf.writeByte(' ');
if (ad.decl.dim == 1 && (*ad.decl)[0].isVisibilityDeclaration)
visit(cast(AttribDeclaration)(*ad.decl)[0]);
else
visit(cast(AttribDeclaration)d);
}
override void visit(AlignDeclaration d)
{
if (d.exps)
{
foreach (i, exp; (*d.exps)[])
{
if (i)
buf.writeByte(' ');
buf.printf("align (%s)", exp.toChars());
}
if (d.decl && d.decl.dim < 2)
buf.writeByte(' ');
}
else
buf.writestring("align ");
visit(d.isAttribDeclaration());
}
override void visit(AnonDeclaration d)
{
buf.writestring(d.isunion ? "union" : "struct");
buf.writenl();
buf.writestring("{");
buf.writenl();
buf.level++;
if (d.decl)
{
foreach (de; *d.decl)
de.accept(this);
}
buf.level--;
buf.writestring("}");
buf.writenl();
}
override void visit(PragmaDeclaration d)
{
buf.writestring("pragma (");
buf.writestring(d.ident.toString());
if (d.args && d.args.dim)
{
buf.writestring(", ");
argsToBuffer(d.args, buf, hgs);
}
buf.writeByte(')');
visit(cast(AttribDeclaration)d);
}
override void visit(ConditionalDeclaration d)
{
d.condition.conditionToBuffer(buf, hgs);
if (d.decl || d.elsedecl)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (d.decl)
{
foreach (de; *d.decl)
de.accept(this);
}
buf.level--;
buf.writeByte('}');
if (d.elsedecl)
{
buf.writenl();
buf.writestring("else");
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (de; *d.elsedecl)
de.accept(this);
buf.level--;
buf.writeByte('}');
}
}
else
buf.writeByte(':');
buf.writenl();
}
override void visit(StaticForeachDeclaration s)
{
void foreachWithoutBody(ForeachStatement s)
{
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
foreach (i, p; *s.parameters)
{
if (i)
buf.writestring(", ");
if (stcToBuffer(buf, p.storageClass))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
}
buf.writestring("; ");
s.aggr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
void foreachRangeWithoutBody(ForeachRangeStatement s)
{
/* s.op ( prm ; lwr .. upr )
*/
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
if (s.prm.type)
typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
else
buf.writestring(s.prm.ident.toString());
buf.writestring("; ");
s.lwr.expressionToBuffer(buf, hgs);
buf.writestring(" .. ");
s.upr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
buf.writestring("static ");
if (s.sfe.aggrfe)
{
foreachWithoutBody(s.sfe.aggrfe);
}
else
{
assert(s.sfe.rangefe);
foreachRangeWithoutBody(s.sfe.rangefe);
}
buf.writeByte('{');
buf.writenl();
buf.level++;
visit(cast(AttribDeclaration)s);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(CompileDeclaration d)
{
buf.writestring("mixin(");
argsToBuffer(d.exps, buf, hgs, null);
buf.writestring(");");
buf.writenl();
}
override void visit(UserAttributeDeclaration d)
{
buf.writestring("@(");
argsToBuffer(d.atts, buf, hgs);
buf.writeByte(')');
visit(cast(AttribDeclaration)d);
}
override void visit(TemplateDeclaration d)
{
version (none)
{
// Should handle template functions for doc generation
if (onemember && onemember.isFuncDeclaration())
buf.writestring("foo ");
}
if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
return;
if (hgs.ddoc)
buf.writestring(d.kind());
else
buf.writestring("template");
buf.writeByte(' ');
buf.writestring(d.ident.toString());
buf.writeByte('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
buf.writeByte(')');
visitTemplateConstraint(d.constraint);
if (hgs.hdrgen || hgs.fullDump)
{
hgs.tpltMember++;
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
hgs.tpltMember--;
}
}
bool visitEponymousMember(TemplateDeclaration d)
{
if (!d.members || d.members.dim != 1)
return false;
Dsymbol onemember = (*d.members)[0];
if (onemember.ident != d.ident)
return false;
if (FuncDeclaration fd = onemember.isFuncDeclaration())
{
assert(fd.type);
if (stcToBuffer(buf, fd.storage_class))
buf.writeByte(' ');
functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
visitTemplateConstraint(d.constraint);
hgs.tpltMember++;
bodyToBuffer(fd);
hgs.tpltMember--;
return true;
}
if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
{
buf.writestring(ad.kind());
buf.writeByte(' ');
buf.writestring(ad.ident.toString());
buf.writeByte('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
buf.writeByte(')');
visitTemplateConstraint(d.constraint);
visitBaseClasses(ad.isClassDeclaration());
hgs.tpltMember++;
if (ad.members)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *ad.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
}
else
buf.writeByte(';');
buf.writenl();
hgs.tpltMember--;
return true;
}
if (VarDeclaration vd = onemember.isVarDeclaration())
{
if (d.constraint)
return false;
if (stcToBuffer(buf, vd.storage_class))
buf.writeByte(' ');
if (vd.type)
typeToBuffer(vd.type, vd.ident, buf, hgs);
else
buf.writestring(vd.ident.toString());
buf.writeByte('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
buf.writeByte(')');
if (vd._init)
{
buf.writestring(" = ");
ExpInitializer ie = vd._init.isExpInitializer();
if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
(cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
else
vd._init.initializerToBuffer(buf, hgs);
}
buf.writeByte(';');
buf.writenl();
return true;
}
return false;
}
void visitTemplateParameters(TemplateParameters* parameters)
{
if (!parameters || !parameters.dim)
return;
foreach (i, p; *parameters)
{
if (i)
buf.writestring(", ");
p.templateParameterToBuffer(buf, hgs);
}
}
void visitTemplateConstraint(Expression constraint)
{
if (!constraint)
return;
buf.writestring(" if (");
constraint.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
override void visit(TemplateInstance ti)
{
buf.writestring(ti.name.toChars());
tiargsToBuffer(ti, buf, hgs);
if (hgs.fullDump)
{
buf.writenl();
dumpTemplateInstance(ti, buf, hgs);
}
}
override void visit(TemplateMixin tm)
{
buf.writestring("mixin ");
typeToBuffer(tm.tqual, null, buf, hgs);
tiargsToBuffer(tm, buf, hgs);
if (tm.ident && memcmp(tm.ident.toChars(), cast(const(char)*)"__mixin", 7) != 0)
{
buf.writeByte(' ');
buf.writestring(tm.ident.toString());
}
buf.writeByte(';');
buf.writenl();
if (hgs.fullDump)
dumpTemplateInstance(tm, buf, hgs);
}
override void visit(EnumDeclaration d)
{
auto oldInEnumDecl = hgs.inEnumDecl;
scope(exit) hgs.inEnumDecl = oldInEnumDecl;
hgs.inEnumDecl = d;
buf.writestring("enum ");
if (d.ident)
{
buf.writestring(d.ident.toString());
}
if (d.memtype)
{
buf.writestring(" : ");
typeToBuffer(d.memtype, null, buf, hgs);
}
if (!d.members)
{
buf.writeByte(';');
buf.writenl();
return;
}
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (em; *d.members)
{
if (!em)
continue;
em.accept(this);
buf.writeByte(',');
buf.writenl();
}
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(Nspace d)
{
buf.writestring("extern (C++, ");
buf.writestring(d.ident.toString());
buf.writeByte(')');
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(StructDeclaration d)
{
buf.writestring(d.kind());
buf.writeByte(' ');
if (!d.isAnonymous())
buf.writestring(d.toChars());
if (!d.members)
{
buf.writeByte(';');
buf.writenl();
return;
}
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(ClassDeclaration d)
{
if (!d.isAnonymous())
{
buf.writestring(d.kind());
buf.writeByte(' ');
buf.writestring(d.ident.toString());
}
visitBaseClasses(d);
if (d.members)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
}
else
buf.writeByte(';');
buf.writenl();
}
void visitBaseClasses(ClassDeclaration d)
{
if (!d || !d.baseclasses.dim)
return;
if (!d.isAnonymous())
buf.writestring(" : ");
foreach (i, b; *d.baseclasses)
{
if (i)
buf.writestring(", ");
typeToBuffer(b.type, null, buf, hgs);
}
}
override void visit(AliasDeclaration d)
{
if (d.storage_class & STC.local)
return;
buf.writestring("alias ");
if (d.aliassym)
{
buf.writestring(d.ident.toString());
buf.writestring(" = ");
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
d.aliassym.accept(this);
}
else if (d.type.ty == Tfunction)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
typeToBuffer(d.type, d.ident, buf, hgs);
}
else if (d.ident)
{
hgs.declstring = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring);
buf.writestring(d.ident.toString());
buf.writestring(" = ");
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
typeToBuffer(d.type, null, buf, hgs);
hgs.declstring = false;
}
buf.writeByte(';');
buf.writenl();
}
override void visit(AliasAssign d)
{
buf.writestring(d.ident.toString());
buf.writestring(" = ");
if (d.aliassym)
d.aliassym.accept(this);
else // d.type
typeToBuffer(d.type, null, buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(VarDeclaration d)
{
if (d.storage_class & STC.local)
return;
visitVarDecl(d, false);
buf.writeByte(';');
buf.writenl();
}
void visitVarDecl(VarDeclaration v, bool anywritten)
{
if (anywritten)
{
buf.writestring(", ");
buf.writestring(v.ident.toString());
}
else
{
if (stcToBuffer(buf, v.storage_class))
buf.writeByte(' ');
if (v.type)
typeToBuffer(v.type, v.ident, buf, hgs);
else
buf.writestring(v.ident.toString());
}
if (v._init)
{
buf.writestring(" = ");
auto ie = v._init.isExpInitializer();
if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
(cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
else
v._init.initializerToBuffer(buf, hgs);
}
}
override void visit(FuncDeclaration f)
{
//printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
if (stcToBuffer(buf, f.storage_class))
buf.writeByte(' ');
auto tf = cast(TypeFunction)f.type;
typeToBuffer(tf, f.ident, buf, hgs);
if (hgs.hdrgen)
{
// if the return type is missing (e.g. ref functions or auto)
if (!tf.next || f.storage_class & STC.auto_)
{
hgs.autoMember++;
bodyToBuffer(f);
hgs.autoMember--;
}
else if (hgs.tpltMember == 0 && global.params.hdrStripPlainFunctions)
{
buf.writeByte(';');
buf.writenl();
}
else
bodyToBuffer(f);
}
else
bodyToBuffer(f);
}
void bodyToBuffer(FuncDeclaration f)
{
if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember))
{
buf.writeByte(';');
buf.writenl();
return;
}
const savetlpt = hgs.tpltMember;
const saveauto = hgs.autoMember;
hgs.tpltMember = 0;
hgs.autoMember = 0;
buf.writenl();
bool requireDo = false;
// in{}
if (f.frequires)
{
foreach (frequire; *f.frequires)
{
buf.writestring("in");
if (auto es = frequire.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
requireDo = false;
}
else
{
buf.writenl();
frequire.statementToBuffer(buf, hgs);
requireDo = true;
}
}
}
// out{}
if (f.fensures)
{
foreach (fensure; *f.fensures)
{
buf.writestring("out");
if (auto es = fensure.ensure.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
if (fensure.id)
{
buf.writestring(fensure.id.toString());
}
buf.writestring("; ");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
requireDo = false;
}
else
{
if (fensure.id)
{
buf.writeByte('(');
buf.writestring(fensure.id.toString());
buf.writeByte(')');
}
buf.writenl();
fensure.ensure.statementToBuffer(buf, hgs);
requireDo = true;
}
}
}
if (requireDo)
{
buf.writestring("do");
buf.writenl();
}
buf.writeByte('{');
buf.writenl();
buf.level++;
f.fbody.statementToBuffer(buf, hgs);
buf.level--;
buf.writeByte('}');
buf.writenl();
hgs.tpltMember = savetlpt;
hgs.autoMember = saveauto;
}
override void visit(FuncLiteralDeclaration f)
{
if (f.type.ty == Terror)
{
buf.writestring("__error");
return;
}
if (f.tok != TOK.reserved)
{
buf.writestring(f.kind());
buf.writeByte(' ');
}
TypeFunction tf = cast(TypeFunction)f.type;
if (!f.inferRetType && tf.next)
typeToBuffer(tf.next, null, buf, hgs);
parametersToBuffer(tf.parameterList, buf, hgs);
// https://issues.dlang.org/show_bug.cgi?id=20074
void printAttribute(string str)
{
buf.writeByte(' ');
buf.writestring(str);
}
tf.attributesApply(&printAttribute);
CompoundStatement cs = f.fbody.isCompoundStatement();
Statement s1;
if (f.semanticRun >= PASS.semantic3done && cs)
{
s1 = (*cs.statements)[cs.statements.dim - 1];
}
else
s1 = !cs ? f.fbody : null;
ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
if (rs && rs.exp)
{
buf.writestring(" => ");
rs.exp.expressionToBuffer(buf, hgs);
}
else
{
hgs.tpltMember++;
bodyToBuffer(f);
hgs.tpltMember--;
}
}
override void visit(PostBlitDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("this(this)");
bodyToBuffer(d);
}
override void visit(DtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("~this()");
bodyToBuffer(d);
}
override void visit(StaticCtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
buf.writeByte(' ');
if (d.isSharedStaticCtorDeclaration())
buf.writestring("shared ");
buf.writestring("static this()");
if (hgs.hdrgen && !hgs.tpltMember)
{
buf.writeByte(';');
buf.writenl();
}
else
bodyToBuffer(d);
}
override void visit(StaticDtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
buf.writeByte(' ');
if (d.isSharedStaticDtorDeclaration())
buf.writestring("shared ");
buf.writestring("static ~this()");
if (hgs.hdrgen && !hgs.tpltMember)
{
buf.writeByte(';');
buf.writenl();
}
else
bodyToBuffer(d);
}
override void visit(InvariantDeclaration d)
{
if (hgs.hdrgen)
return;
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("invariant");
if(auto es = d.fbody.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writestring(");");
buf.writenl();
}
else
{
bodyToBuffer(d);
}
}
override void visit(UnitTestDeclaration d)
{
if (hgs.hdrgen)
return;
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("unittest");
bodyToBuffer(d);
}
override void visit(BitFieldDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
Identifier id = d.isAnonymous() ? null : d.ident;
typeToBuffer(d.type, id, buf, hgs);
buf.writestring(" : ");
d.width.expressionToBuffer(buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(NewDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
buf.writeByte(' ');
buf.writestring("new();");
}
override void visit(Module m)
{
moduleToBuffer2(m, buf, hgs);
}
}
private extern (C++) final class ExpressionPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
////////////////////////////////////////////////////////////////////////////
override void visit(Expression e)
{
buf.writestring(EXPtoString(e.op));
}
override void visit(IntegerExp e)
{
const dinteger_t v = e.toInteger();
if (e.type)
{
Type t = e.type;
L1:
switch (t.ty)
{
case Tenum:
{
TypeEnum te = cast(TypeEnum)t;
auto sym = te.sym;
if (sym && sym.members && (!hgs.inEnumDecl || hgs.inEnumDecl != sym))
{
foreach (em; *sym.members)
{
if ((cast(EnumMember)em).value.toInteger == v)
{
buf.printf("%s.%s", sym.toChars(), em.ident.toChars());
return ;
}
}
}
buf.printf("cast(%s)", te.sym.toChars());
t = te.sym.memtype;
goto L1;
}
case Tchar:
case Twchar:
case Tdchar:
{
const o = buf.length;
writeSingleCharLiteral(*buf, cast(dchar) v);
if (hgs.ddoc)
escapeDdocString(buf, o);
break;
}
case Tint8:
buf.writestring("cast(byte)");
goto L2;
case Tint16:
buf.writestring("cast(short)");
goto L2;
case Tint32:
L2:
buf.printf("%d", cast(int)v);
break;
case Tuns8:
buf.writestring("cast(ubyte)");
goto case Tuns32;
case Tuns16:
buf.writestring("cast(ushort)");
goto case Tuns32;
case Tuns32:
buf.printf("%uu", cast(uint)v);
break;
case Tint64:
buf.printf("%lldL", v);
break;
case Tuns64:
buf.printf("%lluLU", v);
break;
case Tbool:
buf.writestring(v ? "true" : "false");
break;
case Tpointer:
buf.writestring("cast(");
buf.writestring(t.toChars());
buf.writeByte(')');
if (target.ptrsize == 8)
goto case Tuns64;
else if (target.ptrsize == 4 ||
target.ptrsize == 2)
goto case Tuns32;
else
assert(0);
case Tvoid:
buf.writestring("cast(void)0");
break;
default:
/* This can happen if errors, such as
* the type is painted on like in fromConstInitializer().
*/
if (!global.errors)
{
assert(0);
}
break;
}
}
else if (v & 0x8000000000000000L)
buf.printf("0x%llx", v);
else
buf.print(v);
}
override void visit(ErrorExp e)
{
buf.writestring("__error");
}
override void visit(VoidInitExp e)
{
buf.writestring("__void");
}
void floatToBuffer(Type type, real_t value)
{
.floatToBuffer(type, value, buf, hgs.hdrgen);
}
override void visit(RealExp e)
{
floatToBuffer(e.type, e.value);
}
override void visit(ComplexExp e)
{
/* Print as:
* (re+imi)
*/
buf.writeByte('(');
floatToBuffer(e.type, creall(e.value));
buf.writeByte('+');
floatToBuffer(e.type, cimagl(e.value));
buf.writestring("i)");
}
override void visit(IdentifierExp e)
{
if (hgs.hdrgen || hgs.ddoc)
buf.writestring(e.ident.toHChars2());
else
buf.writestring(e.ident.toString());
}
override void visit(DsymbolExp e)
{
buf.writestring(e.s.toChars());
}
override void visit(ThisExp e)
{
buf.writestring("this");
}
override void visit(SuperExp e)
{
buf.writestring("super");
}
override void visit(NullExp e)
{
buf.writestring("null");
}
override void visit(StringExp e)
{
buf.writeByte('"');
const o = buf.length;
foreach (i; 0 .. e.len)
{
writeCharLiteral(*buf, e.getCodeUnit(i));
}
if (hgs.ddoc)
escapeDdocString(buf, o);
buf.writeByte('"');
if (e.postfix)
buf.writeByte(e.postfix);
}
override void visit(ArrayLiteralExp e)
{
buf.writeByte('[');
argsToBuffer(e.elements, buf, hgs, e.basis);
buf.writeByte(']');
}
override void visit(AssocArrayLiteralExp e)
{
buf.writeByte('[');
foreach (i, key; *e.keys)
{
if (i)
buf.writestring(", ");
expToBuffer(key, PREC.assign, buf, hgs);
buf.writeByte(':');
auto value = (*e.values)[i];
expToBuffer(value, PREC.assign, buf, hgs);
}
buf.writeByte(']');
}
override void visit(StructLiteralExp e)
{
buf.writestring(e.sd.toChars());
buf.writeByte('(');
// CTFE can generate struct literals that contain an AddrExp pointing
// to themselves, need to avoid infinite recursion:
// struct S { this(int){ this.s = &this; } S* s; }
// const foo = new S(0);
if (e.stageflags & stageToCBuffer)
buf.writestring("<recursion>");
else
{
const old = e.stageflags;
e.stageflags |= stageToCBuffer;
argsToBuffer(e.elements, buf, hgs);
e.stageflags = old;
}
buf.writeByte(')');
}
override void visit(CompoundLiteralExp e)
{
buf.writeByte('(');
typeToBuffer(e.type, null, buf, hgs);
buf.writeByte(')');
e.initializer.initializerToBuffer(buf, hgs);
}
override void visit(TypeExp e)
{
typeToBuffer(e.type, null, buf, hgs);
}
override void visit(ScopeExp e)
{
if (e.sds.isTemplateInstance())
{
e.sds.dsymbolToBuffer(buf, hgs);
}
else if (hgs !is null && hgs.ddoc)
{
// fixes bug 6491
if (auto m = e.sds.isModule())
buf.writestring(m.md.toChars());
else
buf.writestring(e.sds.toChars());
}
else
{
buf.writestring(e.sds.kind());
buf.writeByte(' ');
buf.writestring(e.sds.toChars());
}
}
override void visit(TemplateExp e)
{
buf.writestring(e.td.toChars());
}
override void visit(NewExp e)
{
if (e.thisexp)
{
expToBuffer(e.thisexp, PREC.primary, buf, hgs);
buf.writeByte('.');
}
buf.writestring("new ");
typeToBuffer(e.newtype, null, buf, hgs);
if (e.arguments && e.arguments.dim)
{
buf.writeByte('(');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(')');
}
}
override void visit(NewAnonClassExp e)
{
if (e.thisexp)
{
expToBuffer(e.thisexp, PREC.primary, buf, hgs);
buf.writeByte('.');
}
buf.writestring("new");
buf.writestring(" class ");
if (e.arguments && e.arguments.dim)
{
buf.writeByte('(');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(')');
}
if (e.cd)
e.cd.dsymbolToBuffer(buf, hgs);
}
override void visit(SymOffExp e)
{
if (e.offset)
buf.printf("(& %s%+lld)", e.var.toChars(), e.offset);
else if (e.var.isTypeInfoDeclaration())
buf.writestring(e.var.toChars());
else
buf.printf("& %s", e.var.toChars());
}
override void visit(VarExp e)
{
buf.writestring(e.var.toChars());
}
override void visit(OverExp e)
{
buf.writestring(e.vars.ident.toString());
}
override void visit(TupleExp e)
{
if (e.e0)
{
buf.writeByte('(');
e.e0.accept(this);
buf.writestring(", tuple(");
argsToBuffer(e.exps, buf, hgs);
buf.writestring("))");
}
else
{
buf.writestring("tuple(");
argsToBuffer(e.exps, buf, hgs);
buf.writeByte(')');
}
}
override void visit(FuncExp e)
{
e.fd.dsymbolToBuffer(buf, hgs);
//buf.writestring(e.fd.toChars());
}
override void visit(DeclarationExp e)
{
/* Normal dmd execution won't reach here - regular variable declarations
* are handled in visit(ExpStatement), so here would be used only when
* we'll directly call Expression.toChars() for debugging.
*/
if (e.declaration)
{
if (auto var = e.declaration.isVarDeclaration())
{
// For debugging use:
// - Avoid printing newline.
// - Intentionally use the format (Type var;)
// which isn't correct as regular D code.
buf.writeByte('(');
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
v.visitVarDecl(var, false);
buf.writeByte(';');
buf.writeByte(')');
}
else e.declaration.dsymbolToBuffer(buf, hgs);
}
}
override void visit(TypeidExp e)
{
buf.writestring("typeid(");
objectToBuffer(e.obj, buf, hgs);
buf.writeByte(')');
}
override void visit(TraitsExp e)
{
buf.writestring("__traits(");
if (e.ident)
buf.writestring(e.ident.toString());
if (e.args)
{
foreach (arg; *e.args)
{
buf.writestring(", ");
objectToBuffer(arg, buf, hgs);
}
}
buf.writeByte(')');
}
override void visit(HaltExp e)
{
buf.writestring("halt");
}
override void visit(IsExp e)
{
buf.writestring("is(");
typeToBuffer(e.targ, e.id, buf, hgs);
if (e.tok2 != TOK.reserved)
{
buf.printf(" %s %s", Token.toChars(e.tok), Token.toChars(e.tok2));
}
else if (e.tspec)
{
if (e.tok == TOK.colon)
buf.writestring(" : ");
else
buf.writestring(" == ");
typeToBuffer(e.tspec, null, buf, hgs);
}
if (e.parameters && e.parameters.dim)
{
buf.writestring(", ");
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
v.visitTemplateParameters(e.parameters);
}
buf.writeByte(')');
}
override void visit(UnaExp e)
{
buf.writestring(EXPtoString(e.op));
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
override void visit(BinExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writeByte(' ');
buf.writestring(EXPtoString(e.op));
buf.writeByte(' ');
expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
}
override void visit(CommaExp e)
{
// CommaExp is generated by the compiler so it shouldn't
// appear in error messages or header files.
// For now, this treats the case where the compiler
// generates CommaExp for temporaries by calling
// the `sideeffect.copyToTemp` function.
auto ve = e.e2.isVarExp();
// not a CommaExp introduced for temporaries, go on
// the old path
if (!ve || !(ve.var.storage_class & STC.temp))
{
visit(cast(BinExp)e);
return;
}
// CommaExp that contain temporaries inserted via
// `copyToTemp` are usually of the form
// ((T __temp = exp), __tmp).
// Asserts are here to easily spot
// missing cases where CommaExp
// are used for other constructs
auto vd = ve.var.isVarDeclaration();
assert(vd && vd._init);
if (auto ei = vd._init.isExpInitializer())
{
Expression commaExtract;
auto exp = ei.exp;
if (auto ce = exp.isConstructExp())
commaExtract = ce.e2;
else if (auto se = exp.isStructLiteralExp())
commaExtract = se;
if (commaExtract)
{
expToBuffer(commaExtract, precedence[exp.op], buf, hgs);
return;
}
}
// not one of the known cases, go on the old path
visit(cast(BinExp)e);
return;
}
override void visit(MixinExp e)
{
buf.writestring("mixin(");
argsToBuffer(e.exps, buf, hgs, null);
buf.writeByte(')');
}
override void visit(ImportExp e)
{
buf.writestring("import(");
expToBuffer(e.e1, PREC.assign, buf, hgs);
buf.writeByte(')');
}
override void visit(AssertExp e)
{
buf.writestring("assert(");
expToBuffer(e.e1, PREC.assign, buf, hgs);
if (e.msg)
{
buf.writestring(", ");
expToBuffer(e.msg, PREC.assign, buf, hgs);
}
buf.writeByte(')');
}
override void visit(ThrowExp e)
{
buf.writestring("throw ");
expToBuffer(e.e1, PREC.unary, buf, hgs);
}
override void visit(DotIdExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
if (e.arrow)
buf.writestring("->");
else
buf.writeByte('.');
buf.writestring(e.ident.toString());
}
override void visit(DotTemplateExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
buf.writestring(e.td.toChars());
}
override void visit(DotVarExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
buf.writestring(e.var.toChars());
}
override void visit(DotTemplateInstanceExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
e.ti.dsymbolToBuffer(buf, hgs);
}
override void visit(DelegateExp e)
{
buf.writeByte('&');
if (!e.func.isNested() || e.func.needThis())
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
}
buf.writestring(e.func.toChars());
}
override void visit(DotTypeExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
buf.writestring(e.sym.toChars());
}
override void visit(CallExp e)
{
if (e.e1.op == EXP.type)
{
/* Avoid parens around type to prevent forbidden cast syntax:
* (sometype)(arg1)
* This is ok since types in constructor calls
* can never depend on parens anyway
*/
e.e1.accept(this);
}
else
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writeByte('(');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(')');
}
override void visit(PtrExp e)
{
buf.writeByte('*');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
override void visit(DeleteExp e)
{
buf.writestring("delete ");
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
override void visit(CastExp e)
{
buf.writestring("cast(");
if (e.to)
typeToBuffer(e.to, null, buf, hgs);
else
{
MODtoBuffer(buf, e.mod);
}
buf.writeByte(')');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
override void visit(VectorExp e)
{
buf.writestring("cast(");
typeToBuffer(e.to, null, buf, hgs);
buf.writeByte(')');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
override void visit(VectorArrayExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".array");
}
override void visit(SliceExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writeByte('[');
if (e.upr || e.lwr)
{
if (e.lwr)
sizeToBuffer(e.lwr, buf, hgs);
else
buf.writeByte('0');
buf.writestring("..");
if (e.upr)
sizeToBuffer(e.upr, buf, hgs);
else
buf.writeByte('$');
}
buf.writeByte(']');
}
override void visit(ArrayLengthExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".length");
}
override void visit(IntervalExp e)
{
expToBuffer(e.lwr, PREC.assign, buf, hgs);
buf.writestring("..");
expToBuffer(e.upr, PREC.assign, buf, hgs);
}
override void visit(DelegatePtrExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".ptr");
}
override void visit(DelegateFuncptrExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".funcptr");
}
override void visit(ArrayExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('[');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(']');
}
override void visit(DotExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
expToBuffer(e.e2, PREC.primary, buf, hgs);
}
override void visit(IndexExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('[');
sizeToBuffer(e.e2, buf, hgs);
buf.writeByte(']');
}
override void visit(PostExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writestring(EXPtoString(e.op));
}
override void visit(PreExp e)
{
buf.writestring(EXPtoString(e.op));
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
override void visit(RemoveExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".remove(");
expToBuffer(e.e2, PREC.assign, buf, hgs);
buf.writeByte(')');
}
override void visit(CondExp e)
{
expToBuffer(e.econd, PREC.oror, buf, hgs);
buf.writestring(" ? ");
expToBuffer(e.e1, PREC.expr, buf, hgs);
buf.writestring(" : ");
expToBuffer(e.e2, PREC.cond, buf, hgs);
}
override void visit(DefaultInitExp e)
{
buf.writestring(EXPtoString(e.op));
}
override void visit(ClassReferenceExp e)
{
buf.writestring(e.value.toChars());
}
}
/**
* Formats `value` as a literal of type `type` into `buf`.
*
* Params:
* type = literal type (e.g. Tfloat)
* value = value to print
* buf = target buffer
* allowHex = whether hex floating point literals may be used
* for greater accuracy
*/
void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool allowHex)
{
/** sizeof(value)*3 is because each byte of mantissa is max
of 256 (3 characters). The string will be "-M.MMMMe-4932".
(ie, 8 chars more than mantissa). Plus one for trailing \0.
Plus one for rounding. */
const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1;
char[BUFFER_LEN] buffer;
CTFloat.sprint(buffer.ptr, 'g', value);
assert(strlen(buffer.ptr) < BUFFER_LEN);
if (allowHex)
{
real_t r = CTFloat.parse(buffer.ptr);
if (r != value) // if exact duplication
CTFloat.sprint(buffer.ptr, 'a', value);
}
buf.writestring(buffer.ptr);
if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
buf.remove(buf.length() - 1, 1);
if (type)
{
Type t = type.toBasetype();
switch (t.ty)
{
case Tfloat32:
case Timaginary32:
case Tcomplex32:
buf.writeByte('F');
break;
case Tfloat80:
case Timaginary80:
case Tcomplex80:
buf.writeByte('L');
break;
default:
break;
}
if (t.isimaginary())
buf.writeByte('i');
}
}
private void templateParameterToBuffer(TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
tp.accept(v);
}
private extern (C++) final class TemplateParameterPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
override void visit(TemplateTypeParameter tp)
{
buf.writestring(tp.ident.toString());
if (tp.specType)
{
buf.writestring(" : ");
typeToBuffer(tp.specType, null, buf, hgs);
}
if (tp.defaultType)
{
buf.writestring(" = ");
typeToBuffer(tp.defaultType, null, buf, hgs);
}
}
override void visit(TemplateThisParameter tp)
{
buf.writestring("this ");
visit(cast(TemplateTypeParameter)tp);
}
override void visit(TemplateAliasParameter tp)
{
buf.writestring("alias ");
if (tp.specType)
typeToBuffer(tp.specType, tp.ident, buf, hgs);
else
buf.writestring(tp.ident.toString());
if (tp.specAlias)
{
buf.writestring(" : ");
objectToBuffer(tp.specAlias, buf, hgs);
}
if (tp.defaultAlias)
{
buf.writestring(" = ");
objectToBuffer(tp.defaultAlias, buf, hgs);
}
}
override void visit(TemplateValueParameter tp)
{
typeToBuffer(tp.valType, tp.ident, buf, hgs);
if (tp.specValue)
{
buf.writestring(" : ");
tp.specValue.expressionToBuffer(buf, hgs);
}
if (tp.defaultValue)
{
buf.writestring(" = ");
tp.defaultValue.expressionToBuffer(buf, hgs);
}
}
override void visit(TemplateTupleParameter tp)
{
buf.writestring(tp.ident.toString());
buf.writestring("...");
}
}
private void conditionToBuffer(Condition c, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new ConditionPrettyPrintVisitor(buf, hgs);
c.accept(v);
}
private extern (C++) final class ConditionPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
override void visit(DebugCondition c)
{
buf.writestring("debug (");
if (c.ident)
buf.writestring(c.ident.toString());
else
buf.print(c.level);
buf.writeByte(')');
}
override void visit(VersionCondition c)
{
buf.writestring("version (");
if (c.ident)
buf.writestring(c.ident.toString());
else
buf.print(c.level);
buf.writeByte(')');
}
override void visit(StaticIfCondition c)
{
buf.writestring("static if (");
c.exp.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
}
void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new StatementPrettyPrintVisitor(buf, hgs);
(cast() s).accept(v);
}
void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs)
{
typeToBuffer(cast() t, ident, buf, hgs);
}
void toCBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
s.accept(v);
}
// used from TemplateInstance::toChars() and TemplateMixin::toChars()
void toCBufferInstance(const TemplateInstance ti, OutBuffer* buf, bool qualifyTypes = false)
{
HdrGenState hgs;
hgs.fullQual = qualifyTypes;
scope v = new DsymbolPrettyPrintVisitor(buf, &hgs);
v.visit(cast() ti);
}
void toCBuffer(const Initializer iz, OutBuffer* buf, HdrGenState* hgs)
{
initializerToBuffer(cast() iz, buf, hgs);
}
bool stcToBuffer(OutBuffer* buf, StorageClass stc)
{
//printf("stc: %llx\n", stc);
bool result = false;
if (stc & STC.scopeinferred)
{
//buf.writestring("scope-inferred ");
stc &= ~(STC.scope_ | STC.scopeinferred);
}
if (stc & STC.returninferred)
{
//buf.writestring("return-inferred ");
stc &= ~(STC.return_ | STC.returninferred);
}
/* Put scope ref return into a standard order
*/
string rrs;
const isout = (stc & STC.out_) != 0;
//printf("bsr = %d %llx\n", buildScopeRef(stc), stc);
final switch (buildScopeRef(stc))
{
case ScopeRef.None:
case ScopeRef.Scope:
case ScopeRef.Ref:
case ScopeRef.Return:
break;
case ScopeRef.ReturnScope: rrs = "return scope"; goto L1;
case ScopeRef.ReturnRef: rrs = isout ? "return out" : "return ref"; goto L1;
case ScopeRef.RefScope: rrs = isout ? "out scope" : "ref scope"; goto L1;
case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1;
case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1;
L1:
buf.writestring(rrs);
result = true;
stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
break;
}
while (stc)
{
const s = stcToString(stc);
if (!s.length)
break;
if (result)
buf.writeByte(' ');
result = true;
buf.writestring(s);
}
return result;
}
/*************************************************
* Pick off one of the storage classes from stc,
* and return a string representation of it.
* stc is reduced by the one picked.
*/
string stcToString(ref StorageClass stc)
{
static struct SCstring
{
StorageClass stc;
string id;
}
// Note: The identifier needs to be `\0` terminated
// as some code assumes it (e.g. when printing error messages)
static immutable SCstring[] table =
[
SCstring(STC.auto_, Token.toString(TOK.auto_)),
SCstring(STC.scope_, Token.toString(TOK.scope_)),
SCstring(STC.static_, Token.toString(TOK.static_)),
SCstring(STC.extern_, Token.toString(TOK.extern_)),
SCstring(STC.const_, Token.toString(TOK.const_)),
SCstring(STC.final_, Token.toString(TOK.final_)),
SCstring(STC.abstract_, Token.toString(TOK.abstract_)),
SCstring(STC.synchronized_, Token.toString(TOK.synchronized_)),
SCstring(STC.deprecated_, Token.toString(TOK.deprecated_)),
SCstring(STC.override_, Token.toString(TOK.override_)),
SCstring(STC.lazy_, Token.toString(TOK.lazy_)),
SCstring(STC.alias_, Token.toString(TOK.alias_)),
SCstring(STC.out_, Token.toString(TOK.out_)),
SCstring(STC.in_, Token.toString(TOK.in_)),
SCstring(STC.manifest, Token.toString(TOK.enum_)),
SCstring(STC.immutable_, Token.toString(TOK.immutable_)),
SCstring(STC.shared_, Token.toString(TOK.shared_)),
SCstring(STC.nothrow_, Token.toString(TOK.nothrow_)),
SCstring(STC.wild, Token.toString(TOK.inout_)),
SCstring(STC.pure_, Token.toString(TOK.pure_)),
SCstring(STC.ref_, Token.toString(TOK.ref_)),
SCstring(STC.return_, Token.toString(TOK.return_)),
SCstring(STC.gshared, Token.toString(TOK.gshared)),
SCstring(STC.nogc, "@nogc"),
SCstring(STC.live, "@live"),
SCstring(STC.property, "@property"),
SCstring(STC.safe, "@safe"),
SCstring(STC.trusted, "@trusted"),
SCstring(STC.system, "@system"),
SCstring(STC.disable, "@disable"),
SCstring(STC.future, "@__future"),
SCstring(STC.local, "__local"),
];
foreach (ref entry; table)
{
const StorageClass tbl = entry.stc;
assert(tbl & STC.visibleStorageClasses);
if (stc & tbl)
{
stc &= ~tbl;
return entry.id;
}
}
//printf("stc = %llx\n", stc);
return null;
}
private void linkageToBuffer(OutBuffer* buf, LINK linkage)
{
const s = linkageToString(linkage);
if (s.length)
{
buf.writestring("extern (");
buf.writestring(s);
buf.writeByte(')');
}
}
const(char)* linkageToChars(LINK linkage)
{
/// Works because we return a literal
return linkageToString(linkage).ptr;
}
string linkageToString(LINK linkage) pure nothrow
{
final switch (linkage)
{
case LINK.default_:
return null;
case LINK.d:
return "D";
case LINK.c:
return "C";
case LINK.cpp:
return "C++";
case LINK.windows:
return "Windows";
case LINK.objc:
return "Objective-C";
case LINK.system:
return "System";
}
}
void visibilityToBuffer(OutBuffer* buf, Visibility vis)
{
buf.writestring(visibilityToString(vis.kind));
if (vis.kind == Visibility.Kind.package_ && vis.pkg)
{
buf.writeByte('(');
buf.writestring(vis.pkg.toPrettyChars(true));
buf.writeByte(')');
}
}
/**
* Returns:
* a human readable representation of `kind`
*/
const(char)* visibilityToChars(Visibility.Kind kind)
{
// Null terminated because we return a literal
return visibilityToString(kind).ptr;
}
/// Ditto
extern (D) string visibilityToString(Visibility.Kind kind) nothrow pure
{
final switch (kind)
{
case Visibility.Kind.undefined:
return null;
case Visibility.Kind.none:
return "none";
case Visibility.Kind.private_:
return "private";
case Visibility.Kind.package_:
return "package";
case Visibility.Kind.protected_:
return "protected";
case Visibility.Kind.public_:
return "public";
case Visibility.Kind.export_:
return "export";
}
}
// Print the full function signature with correct ident, attributes and template args
void functionToBufferFull(TypeFunction tf, OutBuffer* buf, const Identifier ident, HdrGenState* hgs, TemplateDeclaration td)
{
//printf("TypeFunction::toCBuffer() this = %p\n", this);
visitFuncIdentWithPrefix(tf, ident, td, buf, hgs);
}
// ident is inserted before the argument list and will be "function" or "delegate" for a type
void functionToBufferWithIdent(TypeFunction tf, OutBuffer* buf, const(char)* ident, bool isStatic)
{
HdrGenState hgs;
visitFuncIdentWithPostfix(tf, ident.toDString(), buf, &hgs, isStatic);
}
void toCBuffer(const Expression e, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
(cast() e).accept(v);
}
/**************************************************
* Write out argument types to buf.
*/
void argExpTypesToCBuffer(OutBuffer* buf, Expressions* arguments)
{
if (!arguments || !arguments.dim)
return;
HdrGenState hgs;
foreach (i, arg; *arguments)
{
if (i)
buf.writestring(", ");
typeToBuffer(arg.type, null, buf, &hgs);
}
}
void toCBuffer(const TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
(cast() tp).accept(v);
}
void arrayObjectsToBuffer(OutBuffer* buf, Objects* objects)
{
if (!objects || !objects.dim)
return;
HdrGenState hgs;
foreach (i, o; *objects)
{
if (i)
buf.writestring(", ");
objectToBuffer(o, buf, &hgs);
}
}
/*************************************************************
* Pretty print function parameters.
* Params:
* pl = parameter list to print
* Returns: Null-terminated string representing parameters.
*/
extern (C++) const(char)* parametersTypeToChars(ParameterList pl)
{
OutBuffer buf;
HdrGenState hgs;
parametersToBuffer(pl, &buf, &hgs);
return buf.extractChars();
}
/*************************************************************
* Pretty print function parameter.
* Params:
* parameter = parameter to print.
* tf = TypeFunction which holds parameter.
* fullQual = whether to fully qualify types.
* Returns: Null-terminated string representing parameters.
*/
const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQual)
{
OutBuffer buf;
HdrGenState hgs;
hgs.fullQual = fullQual;
parameterToBuffer(parameter, &buf, &hgs);
if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.dim - 1])
{
buf.writestring("...");
}
return buf.extractChars();
}
/*************************************************
* Write ParameterList to buffer.
* Params:
* pl = parameter list to serialize
* buf = buffer to write it to
* hgs = context
*/
private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* hgs)
{
buf.writeByte('(');
foreach (i; 0 .. pl.length)
{
if (i)
buf.writestring(", ");
pl[i].parameterToBuffer(buf, hgs);
}
final switch (pl.varargs)
{
case VarArg.none:
break;
case VarArg.variadic:
if (pl.length)
buf.writestring(", ");
if (stcToBuffer(buf, pl.stc))
buf.writeByte(' ');
goto case VarArg.typesafe;
case VarArg.typesafe:
buf.writestring("...");
break;
}
buf.writeByte(')');
}
/***********************************************************
* Write parameter `p` to buffer `buf`.
* Params:
* p = parameter to serialize
* buf = buffer to write it to
* hgs = context
*/
private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
{
if (p.userAttribDecl)
{
buf.writeByte('@');
bool isAnonymous = p.userAttribDecl.atts.dim > 0 && !(*p.userAttribDecl.atts)[0].isCallExp();
if (isAnonymous)
buf.writeByte('(');
argsToBuffer(p.userAttribDecl.atts, buf, hgs);
if (isAnonymous)
buf.writeByte(')');
buf.writeByte(' ');
}
if (p.storageClass & STC.auto_)
buf.writestring("auto ");
StorageClass stc = p.storageClass;
if (p.storageClass & STC.in_)
{
buf.writestring("in ");
if (global.params.previewIn && p.storageClass & STC.ref_)
stc &= ~STC.ref_;
}
else if (p.storageClass & STC.lazy_)
buf.writestring("lazy ");
else if (p.storageClass & STC.alias_)
buf.writestring("alias ");
if (p.type && p.type.mod & MODFlags.shared_)
stc &= ~STC.shared_;
if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ |
STC.return_ | STC.returninferred | STC.scope_ | STC.scopeinferred | STC.out_ | STC.ref_ | STC.returnScope)))
buf.writeByte(' ');
if (p.storageClass & STC.alias_)
{
if (p.ident)
buf.writestring(p.ident.toString());
}
else if (p.type.ty == Tident &&
(cast(TypeIdentifier)p.type).ident.toString().length > 3 &&
strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0)
{
// print parameter name, instead of undetermined type parameter
buf.writestring(p.ident.toString());
}
else
{
typeToBuffer(p.type, p.ident, buf, hgs, (stc & STC.in_) ? MODFlags.const_ : 0);
}
if (p.defaultArg)
{
buf.writestring(" = ");
p.defaultArg.expToBuffer(PREC.assign, buf, hgs);
}
}
/**************************************************
* Write out argument list to buf.
*/
private void argsToBuffer(Expressions* expressions, OutBuffer* buf, HdrGenState* hgs, Expression basis = null)
{
if (!expressions || !expressions.dim)
return;
version (all)
{
foreach (i, el; *expressions)
{
if (i)
buf.writestring(", ");
if (!el)
el = basis;
if (el)
expToBuffer(el, PREC.assign, buf, hgs);
}
}
else
{
// Sparse style formatting, for debug use only
// [0..dim: basis, 1: e1, 5: e5]
if (basis)
{
buf.writestring("0..");
buf.print(expressions.dim);
buf.writestring(": ");
expToBuffer(basis, PREC.assign, buf, hgs);
}
foreach (i, el; *expressions)
{
if (el)
{
if (basis)
{
buf.writestring(", ");
buf.print(i);
buf.writestring(": ");
}
else if (i)
buf.writestring(", ");
expToBuffer(el, PREC.assign, buf, hgs);
}
}
}
}
private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
{
if (e.type == Type.tsize_t)
{
Expression ex = (e.op == EXP.cast_ ? (cast(CastExp)e).e1 : e);
ex = ex.optimize(WANTvalue);
const dinteger_t uval = ex.op == EXP.int64 ? ex.toInteger() : cast(dinteger_t)-1;
if (cast(sinteger_t)uval >= 0)
{
dinteger_t sizemax = void;
if (target.ptrsize == 8)
sizemax = 0xFFFFFFFFFFFFFFFFUL;
else if (target.ptrsize == 4)
sizemax = 0xFFFFFFFFU;
else if (target.ptrsize == 2)
sizemax = 0xFFFFU;
else
assert(0);
if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFUL)
{
buf.print(uval);
return;
}
}
}
expToBuffer(e, PREC.assign, buf, hgs);
}
private void expressionToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
e.accept(v);
}
/**************************************************
* Write expression out to buf, but wrap it
* in ( ) if its precedence is less than pr.
*/
private void expToBuffer(Expression e, PREC pr, OutBuffer* buf, HdrGenState* hgs)
{
debug
{
if (precedence[e.op] == PREC.zero)
printf("precedence not defined for token '%s'\n", EXPtoString(e.op).ptr);
}
if (e.op == 0xFF)
{
buf.writestring("<FF>");
return;
}
assert(precedence[e.op] != PREC.zero);
assert(pr != PREC.zero);
/* Despite precedence, we don't allow a<b<c expressions.
* They must be parenthesized.
*/
if (precedence[e.op] < pr || (pr == PREC.rel && precedence[e.op] == pr)
|| (pr >= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel))
{
buf.writeByte('(');
e.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
else
{
e.expressionToBuffer(buf, hgs);
}
}
/**************************************************
* An entry point to pretty-print type.
*/
private void typeToBuffer(Type t, const Identifier ident, OutBuffer* buf, HdrGenState* hgs,
ubyte modMask = 0)
{
if (auto tf = t.isTypeFunction())
{
visitFuncIdentWithPrefix(tf, ident, null, buf, hgs);
return;
}
visitWithMask(t, modMask, buf, hgs);
if (ident)
{
buf.writeByte(' ');
buf.writestring(ident.toString());
}
}
private void visitWithMask(Type t, ubyte modMask, OutBuffer* buf, HdrGenState* hgs)
{
// Tuples and functions don't use the type constructor syntax
if (modMask == t.mod || t.ty == Tfunction || t.ty == Ttuple)
{
typeToBufferx(t, buf, hgs);
}
else
{
ubyte m = t.mod & ~(t.mod & modMask);
if (m & MODFlags.shared_)
{
MODtoBuffer(buf, MODFlags.shared_);
buf.writeByte('(');
}
if (m & MODFlags.wild)
{
MODtoBuffer(buf, MODFlags.wild);
buf.writeByte('(');
}
if (m & (MODFlags.const_ | MODFlags.immutable_))
{
MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_));
buf.writeByte('(');
}
typeToBufferx(t, buf, hgs);
if (m & (MODFlags.const_ | MODFlags.immutable_))
buf.writeByte(')');
if (m & MODFlags.wild)
buf.writeByte(')');
if (m & MODFlags.shared_)
buf.writeByte(')');
}
}
private void dumpTemplateInstance(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
{
buf.writeByte('{');
buf.writenl();
buf.level++;
if (ti.aliasdecl)
{
ti.aliasdecl.dsymbolToBuffer(buf, hgs);
buf.writenl();
}
else if (ti.members)
{
foreach(m;*ti.members)
m.dsymbolToBuffer(buf, hgs);
}
buf.level--;
buf.writeByte('}');
buf.writenl();
}
private void tiargsToBuffer(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
{
buf.writeByte('!');
if (ti.nest)
{
buf.writestring("(...)");
return;
}
if (!ti.tiargs)
{
buf.writestring("()");
return;
}
if (ti.tiargs.dim == 1)
{
RootObject oarg = (*ti.tiargs)[0];
if (Type t = isType(oarg))
{
if (t.equals(Type.tstring) || t.equals(Type.twstring) || t.equals(Type.tdstring) || t.mod == 0 && (t.isTypeBasic() || t.ty == Tident && (cast(TypeIdentifier)t).idents.dim == 0))
{
buf.writestring(t.toChars());
return;
}
}
else if (Expression e = isExpression(oarg))
{
if (e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_)
{
buf.writestring(e.toChars());
return;
}
}
}
buf.writeByte('(');
ti.nestUp();
foreach (i, arg; *ti.tiargs)
{
if (i)
buf.writestring(", ");
objectToBuffer(arg, buf, hgs);
}
ti.nestDown();
buf.writeByte(')');
}
/****************************************
* This makes a 'pretty' version of the template arguments.
* It's analogous to genIdent() which makes a mangled version.
*/
private void objectToBuffer(RootObject oarg, OutBuffer* buf, HdrGenState* hgs)
{
//printf("objectToBuffer()\n");
/* The logic of this should match what genIdent() does. The _dynamic_cast()
* function relies on all the pretty strings to be unique for different classes
* See https://issues.dlang.org/show_bug.cgi?id=7375
* Perhaps it would be better to demangle what genIdent() does.
*/
if (auto t = isType(oarg))
{
//printf("\tt: %s ty = %d\n", t.toChars(), t.ty);
typeToBuffer(t, null, buf, hgs);
}
else if (auto e = isExpression(oarg))
{
if (e.op == EXP.variable)
e = e.optimize(WANTvalue); // added to fix https://issues.dlang.org/show_bug.cgi?id=7375
expToBuffer(e, PREC.assign, buf, hgs);
}
else if (Dsymbol s = isDsymbol(oarg))
{
const p = s.ident ? s.ident.toChars() : s.toChars();
buf.writestring(p);
}
else if (auto v = isTuple(oarg))
{
auto args = &v.objects;
foreach (i, arg; *args)
{
if (i)
buf.writestring(", ");
objectToBuffer(arg, buf, hgs);
}
}
else if (auto p = isParameter(oarg))
{
parameterToBuffer(p, buf, hgs);
}
else if (!oarg)
{
buf.writestring("NULL");
}
else
{
debug
{
printf("bad Object = %p\n", oarg);
}
assert(0);
}
}
private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, OutBuffer* buf, HdrGenState* hgs, bool isStatic)
{
if (t.inuse)
{
t.inuse = 2; // flag error to caller
return;
}
t.inuse++;
if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
{
linkageToBuffer(buf, t.linkage);
buf.writeByte(' ');
}
if (t.linkage == LINK.objc && isStatic)
buf.write("static ");
if (t.next)
{
typeToBuffer(t.next, null, buf, hgs);
if (ident)
buf.writeByte(' ');
}
else if (hgs.ddoc)
buf.writestring("auto ");
if (ident)
buf.writestring(ident);
parametersToBuffer(t.parameterList, buf, hgs);
/* Use postfix style for attributes
*/
if (t.mod)
{
buf.writeByte(' ');
MODtoBuffer(buf, t.mod);
}
void dg(string str)
{
buf.writeByte(' ');
buf.writestring(str);
}
t.attributesApply(&dg);
t.inuse--;
}
private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, TemplateDeclaration td,
OutBuffer* buf, HdrGenState* hgs)
{
if (t.inuse)
{
t.inuse = 2; // flag error to caller
return;
}
t.inuse++;
/* Use 'storage class' (prefix) style for attributes
*/
if (t.mod)
{
MODtoBuffer(buf, t.mod);
buf.writeByte(' ');
}
void ignoreReturn(string str)
{
if (str != "return")
{
// don't write 'ref' for ctors
if ((ident == Id.ctor) && str == "ref")
return;
buf.writestring(str);
buf.writeByte(' ');
}
}
t.attributesApply(&ignoreReturn);
if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
{
linkageToBuffer(buf, t.linkage);
buf.writeByte(' ');
}
if (ident && ident.toHChars2() != ident.toChars())
{
// Don't print return type for ctor, dtor, unittest, etc
}
else if (t.next)
{
typeToBuffer(t.next, null, buf, hgs);
if (ident)
buf.writeByte(' ');
}
else if (hgs.ddoc)
buf.writestring("auto ");
if (ident)
buf.writestring(ident.toHChars2());
if (td)
{
buf.writeByte('(');
foreach (i, p; *td.origParameters)
{
if (i)
buf.writestring(", ");
p.templateParameterToBuffer(buf, hgs);
}
buf.writeByte(')');
}
parametersToBuffer(t.parameterList, buf, hgs);
if (t.isreturn)
{
buf.writestring(" return");
}
t.inuse--;
}
private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* hgs)
{
void visitError(ErrorInitializer iz)
{
buf.writestring("__error__");
}
void visitVoid(VoidInitializer iz)
{
buf.writestring("void");
}
void visitStruct(StructInitializer si)
{
//printf("StructInitializer::toCBuffer()\n");
buf.writeByte('{');
foreach (i, const id; si.field)
{
if (i)
buf.writestring(", ");
if (id)
{
buf.writestring(id.toString());
buf.writeByte(':');
}
if (auto iz = si.value[i])
initializerToBuffer(iz, buf, hgs);
}
buf.writeByte('}');
}
void visitArray(ArrayInitializer ai)
{
buf.writeByte('[');
foreach (i, ex; ai.index)
{
if (i)
buf.writestring(", ");
if (ex)
{
ex.expressionToBuffer(buf, hgs);
buf.writeByte(':');
}
if (auto iz = ai.value[i])
initializerToBuffer(iz, buf, hgs);
}
buf.writeByte(']');
}
void visitExp(ExpInitializer ei)
{
ei.exp.expressionToBuffer(buf, hgs);
}
void visitC(CInitializer ci)
{
buf.writeByte('{');
foreach (i, ref DesigInit di; ci.initializerList)
{
if (i)
buf.writestring(", ");
if (di.designatorList)
{
foreach (ref Designator d; (*di.designatorList)[])
{
if (d.exp)
{
buf.writeByte('[');
toCBuffer(d.exp, buf, hgs);
buf.writeByte(']');
}
else
{
buf.writeByte('.');
buf.writestring(d.ident.toString());
}
}
buf.writeByte('=');
}
initializerToBuffer(di.initializer, buf, hgs);
}
buf.writeByte('}');
}
final switch (inx.kind)
{
case InitKind.error: return visitError (inx.isErrorInitializer ());
case InitKind.void_: return visitVoid (inx.isVoidInitializer ());
case InitKind.struct_: return visitStruct(inx.isStructInitializer());
case InitKind.array: return visitArray (inx.isArrayInitializer ());
case InitKind.exp: return visitExp (inx.isExpInitializer ());
case InitKind.C_: return visitC (inx.isCInitializer ());
}
}
private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
{
void visitType(Type t)
{
printf("t = %p, ty = %d\n", t, t.ty);
assert(0);
}
void visitError(TypeError t)
{
buf.writestring("_error_");
}
void visitBasic(TypeBasic t)
{
//printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
buf.writestring(t.dstring);
}
void visitTraits(TypeTraits t)
{
//printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
t.exp.expressionToBuffer(buf, hgs);
}
void visitVector(TypeVector t)
{
//printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod);
buf.writestring("__vector(");
visitWithMask(t.basetype, t.mod, buf, hgs);
buf.writestring(")");
}
void visitSArray(TypeSArray t)
{
visitWithMask(t.next, t.mod, buf, hgs);
buf.writeByte('[');
sizeToBuffer(t.dim, buf, hgs);
buf.writeByte(']');
}
void visitDArray(TypeDArray t)
{
Type ut = t.castMod(0);
if (hgs.declstring)
goto L1;
if (ut.equals(Type.tstring))
buf.writestring("string");
else if (ut.equals(Type.twstring))
buf.writestring("wstring");
else if (ut.equals(Type.tdstring))
buf.writestring("dstring");
else
{
L1:
visitWithMask(t.next, t.mod, buf, hgs);
buf.writestring("[]");
}
}
void visitAArray(TypeAArray t)
{
visitWithMask(t.next, t.mod, buf, hgs);
buf.writeByte('[');
visitWithMask(t.index, 0, buf, hgs);
buf.writeByte(']');
}
void visitPointer(TypePointer t)
{
//printf("TypePointer::toCBuffer2() next = %d\n", t.next.ty);
if (t.next.ty == Tfunction)
visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "function", buf, hgs, false);
else
{
visitWithMask(t.next, t.mod, buf, hgs);
buf.writeByte('*');
}
}
void visitReference(TypeReference t)
{
visitWithMask(t.next, t.mod, buf, hgs);
buf.writeByte('&');
}
void visitFunction(TypeFunction t)
{
//printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isref);
visitFuncIdentWithPostfix(t, null, buf, hgs, false);
}
void visitDelegate(TypeDelegate t)
{
visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "delegate", buf, hgs, false);
}
void visitTypeQualifiedHelper(TypeQualified t)
{
foreach (id; t.idents)
{
if (id.dyncast() == DYNCAST.dsymbol)
{
buf.writeByte('.');
TemplateInstance ti = cast(TemplateInstance)id;
ti.dsymbolToBuffer(buf, hgs);
}
else if (id.dyncast() == DYNCAST.expression)
{
buf.writeByte('[');
(cast(Expression)id).expressionToBuffer(buf, hgs);
buf.writeByte(']');
}
else if (id.dyncast() == DYNCAST.type)
{
buf.writeByte('[');
typeToBufferx(cast(Type)id, buf, hgs);
buf.writeByte(']');
}
else
{
buf.writeByte('.');
buf.writestring(id.toString());
}
}
}
void visitIdentifier(TypeIdentifier t)
{
buf.writestring(t.ident.toString());
visitTypeQualifiedHelper(t);
}
void visitInstance(TypeInstance t)
{
t.tempinst.dsymbolToBuffer(buf, hgs);
visitTypeQualifiedHelper(t);
}
void visitTypeof(TypeTypeof t)
{
buf.writestring("typeof(");
t.exp.expressionToBuffer(buf, hgs);
buf.writeByte(')');
visitTypeQualifiedHelper(t);
}
void visitReturn(TypeReturn t)
{
buf.writestring("typeof(return)");
visitTypeQualifiedHelper(t);
}
void visitEnum(TypeEnum t)
{
buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
}
void visitStruct(TypeStruct t)
{
// https://issues.dlang.org/show_bug.cgi?id=13776
// Don't use ti.toAlias() to avoid forward reference error
// while printing messages.
TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
if (ti && ti.aliasdecl == t.sym)
buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
else
buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
}
void visitClass(TypeClass t)
{
// https://issues.dlang.org/show_bug.cgi?id=13776
// Don't use ti.toAlias() to avoid forward reference error
// while printing messages.
TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
if (ti && ti.aliasdecl == t.sym)
buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
else
buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
}
void visitTag(TypeTag t)
{
buf.writestring(Token.toChars(t.tok));
buf.writeByte(' ');
if (t.id)
buf.writestring(t.id.toChars());
if (t.tok == TOK.enum_ && t.base.ty != TY.Tint32)
{
buf.writestring(" : ");
visitWithMask(t.base, t.mod, buf, hgs);
}
}
void visitTuple(TypeTuple t)
{
parametersToBuffer(ParameterList(t.arguments, VarArg.none), buf, hgs);
}
void visitSlice(TypeSlice t)
{
visitWithMask(t.next, t.mod, buf, hgs);
buf.writeByte('[');
sizeToBuffer(t.lwr, buf, hgs);
buf.writestring(" .. ");
sizeToBuffer(t.upr, buf, hgs);
buf.writeByte(']');
}
void visitNull(TypeNull t)
{
buf.writestring("typeof(null)");
}
void visitMixin(TypeMixin t)
{
buf.writestring("mixin(");
argsToBuffer(t.exps, buf, hgs, null);
buf.writeByte(')');
}
void visitNoreturn(TypeNoreturn t)
{
buf.writestring("noreturn");
}
switch (t.ty)
{
default: return t.isTypeBasic() ?
visitBasic(cast(TypeBasic)t) :
visitType(t);
case Terror: return visitError(cast(TypeError)t);
case Ttraits: return visitTraits(cast(TypeTraits)t);
case Tvector: return visitVector(cast(TypeVector)t);
case Tsarray: return visitSArray(cast(TypeSArray)t);
case Tarray: return visitDArray(cast(TypeDArray)t);
case Taarray: return visitAArray(cast(TypeAArray)t);
case Tpointer: return visitPointer(cast(TypePointer)t);
case Treference: return visitReference(cast(TypeReference)t);
case Tfunction: return visitFunction(cast(TypeFunction)t);
case Tdelegate: return visitDelegate(cast(TypeDelegate)t);
case Tident: return visitIdentifier(cast(TypeIdentifier)t);
case Tinstance: return visitInstance(cast(TypeInstance)t);
case Ttypeof: return visitTypeof(cast(TypeTypeof)t);
case Treturn: return visitReturn(cast(TypeReturn)t);
case Tenum: return visitEnum(cast(TypeEnum)t);
case Tstruct: return visitStruct(cast(TypeStruct)t);
case Tclass: return visitClass(cast(TypeClass)t);
case Ttuple: return visitTuple (cast(TypeTuple)t);
case Tslice: return visitSlice(cast(TypeSlice)t);
case Tnull: return visitNull(cast(TypeNull)t);
case Tmixin: return visitMixin(cast(TypeMixin)t);
case Tnoreturn: return visitNoreturn(cast(TypeNoreturn)t);
case Ttag: return visitTag(cast(TypeTag)t);
}
}
/****************************************
* Convert EXP to char*.
*/
string EXPtoString(EXP op)
{
static immutable char*[EXP.max + 1] strings =
[
EXP.type : "type",
EXP.error : "error",
EXP.objcClassReference : "class",
EXP.typeof_ : "typeof",
EXP.mixin_ : "mixin",
EXP.import_ : "import",
EXP.dotVariable : "dotvar",
EXP.scope_ : "scope",
EXP.identifier : "identifier",
EXP.this_ : "this",
EXP.super_ : "super",
EXP.int64 : "long",
EXP.float64 : "double",
EXP.complex80 : "creal",
EXP.null_ : "null",
EXP.string_ : "string",
EXP.arrayLiteral : "arrayliteral",
EXP.assocArrayLiteral : "assocarrayliteral",
EXP.classReference : "classreference",
EXP.file : "__FILE__",
EXP.fileFullPath : "__FILE_FULL_PATH__",
EXP.line : "__LINE__",
EXP.moduleString : "__MODULE__",
EXP.functionString : "__FUNCTION__",
EXP.prettyFunction : "__PRETTY_FUNCTION__",
EXP.typeid_ : "typeid",
EXP.is_ : "is",
EXP.assert_ : "assert",
EXP.halt : "halt",
EXP.template_ : "template",
EXP.dSymbol : "symbol",
EXP.function_ : "function",
EXP.variable : "var",
EXP.symbolOffset : "symoff",
EXP.structLiteral : "structLiteral",
EXP.compoundLiteral : "compoundliteral",
EXP.arrayLength : "arraylength",
EXP.delegatePointer : "delegateptr",
EXP.delegateFunctionPointer : "delegatefuncptr",
EXP.remove : "remove",
EXP.tuple : "tuple",
EXP.traits : "__traits",
EXP.default_ : "default",
EXP.overloadSet : "__overloadset",
EXP.void_ : "void",
EXP.vectorArray : "vectorarray",
EXP._Generic : "_Generic",
// post
EXP.dotTemplateInstance : "dotti",
EXP.dotIdentifier : "dotid",
EXP.dotTemplateDeclaration : "dottd",
EXP.dot : ".",
EXP.dotType : "dottype",
EXP.plusPlus : "++",
EXP.minusMinus : "--",
EXP.prePlusPlus : "++",
EXP.preMinusMinus : "--",
EXP.call : "call",
EXP.slice : "..",
EXP.array : "[]",
EXP.index : "[i]",
EXP.delegate_ : "delegate",
EXP.address : "&",
EXP.star : "*",
EXP.negate : "-",
EXP.uadd : "+",
EXP.not : "!",
EXP.tilde : "~",
EXP.delete_ : "delete",
EXP.new_ : "new",
EXP.newAnonymousClass : "newanonclass",
EXP.cast_ : "cast",
EXP.vector : "__vector",
EXP.pow : "^^",
EXP.mul : "*",
EXP.div : "/",
EXP.mod : "%",
EXP.add : "+",
EXP.min : "-",
EXP.concatenate : "~",
EXP.leftShift : "<<",
EXP.rightShift : ">>",
EXP.unsignedRightShift : ">>>",
EXP.lessThan : "<",
EXP.lessOrEqual : "<=",
EXP.greaterThan : ">",
EXP.greaterOrEqual : ">=",
EXP.in_ : "in",
EXP.equal : "==",
EXP.notEqual : "!=",
EXP.identity : "is",
EXP.notIdentity : "!is",
EXP.and : "&",
EXP.xor : "^",
EXP.or : "|",
EXP.andAnd : "&&",
EXP.orOr : "||",
EXP.question : "?",
EXP.assign : "=",
EXP.construct : "=",
EXP.blit : "=",
EXP.addAssign : "+=",
EXP.minAssign : "-=",
EXP.concatenateAssign : "~=",
EXP.concatenateElemAssign : "~=",
EXP.concatenateDcharAssign : "~=",
EXP.mulAssign : "*=",
EXP.divAssign : "/=",
EXP.modAssign : "%=",
EXP.powAssign : "^^=",
EXP.leftShiftAssign : "<<=",
EXP.rightShiftAssign : ">>=",
EXP.unsignedRightShiftAssign : ">>>=",
EXP.andAssign : "&=",
EXP.orAssign : "|=",
EXP.xorAssign : "^=",
EXP.comma : ",",
EXP.declaration : "declaration",
EXP.interval : "interval",
];
const p = strings[op];
if (!p)
{
printf("error: EXP %d has no string\n", op);
return "XXXXX";
//assert(0);
}
assert(p);
return p[0 .. strlen(p)];
}