| /** |
| * 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; |
| int insideFuncBody; |
| |
| 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(')'); |
| |
| // https://issues.dlang.org/show_bug.cgi?id=14690 |
| // Unconditionally perform a full output dump |
| // for `pragma(inline)` declarations. |
| bool savedFullDump = global.params.dihdr.fullOutput; |
| if (d.ident == Id.Pinline) |
| global.params.dihdr.fullOutput = true; |
| |
| visit(cast(AttribDeclaration)d); |
| global.params.dihdr.fullOutput = savedFullDump; |
| } |
| |
| 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(' '); |
| /* |
| https://issues.dlang.org/show_bug.cgi?id=23223 |
| https://issues.dlang.org/show_bug.cgi?id=23222 |
| This special case (initially just for modules) avoids some segfaults |
| and nicer -vcg-ast output. |
| */ |
| if (d.aliassym.isModule()) |
| { |
| buf.writestring(d.aliassym.ident.toString()); |
| } |
| else |
| { |
| 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.dihdr.fullOutput == false && !hgs.insideFuncBody) |
| { |
| if (!f.fbody) |
| { |
| // this can happen on interfaces / abstract functions, see `allowsContractWithoutBody` |
| if (f.fensures || f.frequires) |
| buf.writenl(); |
| contractsToBuffer(f); |
| } |
| buf.writeByte(';'); |
| buf.writenl(); |
| } |
| else |
| bodyToBuffer(f); |
| } |
| else |
| bodyToBuffer(f); |
| } |
| |
| /// Returns: whether `do` is needed to write the function body |
| bool contractsToBuffer(FuncDeclaration f) |
| { |
| 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; |
| } |
| } |
| } |
| return requireDo; |
| } |
| |
| void bodyToBuffer(FuncDeclaration f) |
| { |
| if (!f.fbody || (hgs.hdrgen && global.params.dihdr.fullOutput == false && !hgs.autoMember && !hgs.tpltMember && !hgs.insideFuncBody)) |
| { |
| if (!f.fbody && (f.fensures || f.frequires)) |
| { |
| buf.writenl(); |
| contractsToBuffer(f); |
| } |
| buf.writeByte(';'); |
| buf.writenl(); |
| return; |
| } |
| |
| // there is no way to know if a function is nested |
| // or not after parsing. We need scope information |
| // for that, which is avaible during semantic |
| // analysis. To overcome that, a simple mechanism |
| // is implemented: everytime we print a function |
| // body (templated or not) we increment a counter. |
| // We decredement the counter when we stop |
| // printing the function body. |
| ++hgs.insideFuncBody; |
| scope(exit) { --hgs.insideFuncBody; } |
| |
| const savetlpt = hgs.tpltMember; |
| const saveauto = hgs.autoMember; |
| hgs.tpltMember = 0; |
| hgs.autoMember = 0; |
| buf.writenl(); |
| bool requireDo = contractsToBuffer(f); |
| |
| 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); |
| } |
| } |
| |
| /********************************************* |
| * Print expression to buffer. |
| */ |
| private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hgs) |
| { |
| void visit(Expression e) |
| { |
| buf.writestring(EXPtoString(e.op)); |
| } |
| |
| void visitInteger(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: |
| if (v == long.min) |
| { |
| // https://issues.dlang.org/show_bug.cgi?id=23173 |
| // This is a special case because - is not part of the |
| // integer literal and 9223372036854775808L overflows a long |
| buf.writestring("cast(long)-9223372036854775808"); |
| } |
| else |
| { |
| 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); |
| } |
| |
| void visitError(ErrorExp e) |
| { |
| buf.writestring("__error"); |
| } |
| |
| void visitVoidInit(VoidInitExp e) |
| { |
| buf.writestring("__void"); |
| } |
| |
| void floatToBuffer(Type type, real_t value) |
| { |
| .floatToBuffer(type, value, buf, hgs.hdrgen); |
| } |
| |
| void visitReal(RealExp e) |
| { |
| floatToBuffer(e.type, e.value); |
| } |
| |
| void visitComplex(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)"); |
| } |
| |
| void visitIdentifier(IdentifierExp e) |
| { |
| if (hgs.hdrgen || hgs.ddoc) |
| buf.writestring(e.ident.toHChars2()); |
| else |
| buf.writestring(e.ident.toString()); |
| } |
| |
| void visitDsymbol(DsymbolExp e) |
| { |
| buf.writestring(e.s.toChars()); |
| } |
| |
| void visitThis(ThisExp e) |
| { |
| buf.writestring("this"); |
| } |
| |
| void visitSuper(SuperExp e) |
| { |
| buf.writestring("super"); |
| } |
| |
| void visitNull(NullExp e) |
| { |
| buf.writestring("null"); |
| } |
| |
| void visitString(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); |
| } |
| |
| void visitArrayLiteral(ArrayLiteralExp e) |
| { |
| buf.writeByte('['); |
| argsToBuffer(e.elements, buf, hgs, e.basis); |
| buf.writeByte(']'); |
| } |
| |
| void visitAssocArrayLiteral(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(']'); |
| } |
| |
| void visitStructLiteral(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(')'); |
| } |
| |
| void visitCompoundLiteral(CompoundLiteralExp e) |
| { |
| buf.writeByte('('); |
| typeToBuffer(e.type, null, buf, hgs); |
| buf.writeByte(')'); |
| e.initializer.initializerToBuffer(buf, hgs); |
| } |
| |
| void visitType(TypeExp e) |
| { |
| typeToBuffer(e.type, null, buf, hgs); |
| } |
| |
| void visitScope(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()); |
| } |
| } |
| |
| void visitTemplate(TemplateExp e) |
| { |
| buf.writestring(e.td.toChars()); |
| } |
| |
| void visitNew(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(')'); |
| } |
| } |
| |
| void visitNewAnonClass(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); |
| } |
| |
| void visitSymOff(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()); |
| } |
| |
| void visitVar(VarExp e) |
| { |
| buf.writestring(e.var.toChars()); |
| } |
| |
| void visitOver(OverExp e) |
| { |
| buf.writestring(e.vars.ident.toString()); |
| } |
| |
| void visitTuple(TupleExp e) |
| { |
| if (e.e0) |
| { |
| buf.writeByte('('); |
| e.e0.expressionPrettyPrint(buf, hgs); |
| buf.writestring(", tuple("); |
| argsToBuffer(e.exps, buf, hgs); |
| buf.writestring("))"); |
| } |
| else |
| { |
| buf.writestring("tuple("); |
| argsToBuffer(e.exps, buf, hgs); |
| buf.writeByte(')'); |
| } |
| } |
| |
| void visitFunc(FuncExp e) |
| { |
| e.fd.dsymbolToBuffer(buf, hgs); |
| //buf.writestring(e.fd.toChars()); |
| } |
| |
| void visitDeclaration(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); |
| } |
| } |
| |
| void visitTypeid(TypeidExp e) |
| { |
| buf.writestring("typeid("); |
| objectToBuffer(e.obj, buf, hgs); |
| buf.writeByte(')'); |
| } |
| |
| void visitTraits(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(')'); |
| } |
| |
| void visitHalt(HaltExp e) |
| { |
| buf.writestring("halt"); |
| } |
| |
| void visitIs(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(')'); |
| } |
| |
| void visitUna(UnaExp e) |
| { |
| buf.writestring(EXPtoString(e.op)); |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| } |
| |
| void visitBin(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); |
| } |
| |
| void visitComma(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)) |
| { |
| visitBin(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 |
| visitBin(cast(BinExp)e); |
| return; |
| } |
| |
| void visitMixin(MixinExp e) |
| { |
| buf.writestring("mixin("); |
| argsToBuffer(e.exps, buf, hgs, null); |
| buf.writeByte(')'); |
| } |
| |
| void visitImport(ImportExp e) |
| { |
| buf.writestring("import("); |
| expToBuffer(e.e1, PREC.assign, buf, hgs); |
| buf.writeByte(')'); |
| } |
| |
| void visitAssert(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(')'); |
| } |
| |
| void visitThrow(ThrowExp e) |
| { |
| buf.writestring("throw "); |
| expToBuffer(e.e1, PREC.unary, buf, hgs); |
| } |
| |
| void visitDotId(DotIdExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| if (e.arrow) |
| buf.writestring("->"); |
| else |
| buf.writeByte('.'); |
| buf.writestring(e.ident.toString()); |
| } |
| |
| void visitDotTemplate(DotTemplateExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('.'); |
| buf.writestring(e.td.toChars()); |
| } |
| |
| void visitDotVar(DotVarExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('.'); |
| buf.writestring(e.var.toChars()); |
| } |
| |
| void visitDotTemplateInstance(DotTemplateInstanceExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('.'); |
| e.ti.dsymbolToBuffer(buf, hgs); |
| } |
| |
| void visitDelegate(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()); |
| } |
| |
| void visitDotType(DotTypeExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('.'); |
| buf.writestring(e.sym.toChars()); |
| } |
| |
| void visitCall(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.expressionPrettyPrint(buf, hgs); |
| } |
| else |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| buf.writeByte('('); |
| argsToBuffer(e.arguments, buf, hgs); |
| buf.writeByte(')'); |
| } |
| |
| void visitPtr(PtrExp e) |
| { |
| buf.writeByte('*'); |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| } |
| |
| void visitDelete(DeleteExp e) |
| { |
| buf.writestring("delete "); |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| } |
| |
| void visitCast(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); |
| } |
| |
| void visitVector(VectorExp e) |
| { |
| buf.writestring("cast("); |
| typeToBuffer(e.to, null, buf, hgs); |
| buf.writeByte(')'); |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| } |
| |
| void visitVectorArray(VectorArrayExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writestring(".array"); |
| } |
| |
| void visitSlice(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(']'); |
| } |
| |
| void visitArrayLength(ArrayLengthExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writestring(".length"); |
| } |
| |
| void visitInterval(IntervalExp e) |
| { |
| expToBuffer(e.lwr, PREC.assign, buf, hgs); |
| buf.writestring(".."); |
| expToBuffer(e.upr, PREC.assign, buf, hgs); |
| } |
| |
| void visitDelegatePtr(DelegatePtrExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writestring(".ptr"); |
| } |
| |
| void visitDelegateFuncptr(DelegateFuncptrExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writestring(".funcptr"); |
| } |
| |
| void visitArray(ArrayExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('['); |
| argsToBuffer(e.arguments, buf, hgs); |
| buf.writeByte(']'); |
| } |
| |
| void visitDot(DotExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('.'); |
| expToBuffer(e.e2, PREC.primary, buf, hgs); |
| } |
| |
| void visitIndex(IndexExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writeByte('['); |
| sizeToBuffer(e.e2, buf, hgs); |
| buf.writeByte(']'); |
| } |
| |
| void visitPost(PostExp e) |
| { |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| buf.writestring(EXPtoString(e.op)); |
| } |
| |
| void visitPre(PreExp e) |
| { |
| buf.writestring(EXPtoString(e.op)); |
| expToBuffer(e.e1, precedence[e.op], buf, hgs); |
| } |
| |
| void visitRemove(RemoveExp e) |
| { |
| expToBuffer(e.e1, PREC.primary, buf, hgs); |
| buf.writestring(".remove("); |
| expToBuffer(e.e2, PREC.assign, buf, hgs); |
| buf.writeByte(')'); |
| } |
| |
| void visitCond(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); |
| } |
| |
| void visitDefaultInit(DefaultInitExp e) |
| { |
| buf.writestring(EXPtoString(e.op)); |
| } |
| |
| void visitClassReference(ClassReferenceExp e) |
| { |
| buf.writestring(e.value.toChars()); |
| } |
| |
| switch (e.op) |
| { |
| default: |
| if (auto be = e.isBinExp()) |
| return visitBin(be); |
| else if (auto ue = e.isUnaExp()) |
| return visitUna(ue); |
| else if (auto de = e.isDefaultInitExp()) |
| return visitDefaultInit(e.isDefaultInitExp()); |
| return visit(e); |
| |
| case EXP.int64: return visitInteger(e.isIntegerExp()); |
| case EXP.error: return visitError(e.isErrorExp()); |
| case EXP.void_: return visitVoidInit(e.isVoidInitExp()); |
| case EXP.float64: return visitReal(e.isRealExp()); |
| case EXP.complex80: return visitComplex(e.isComplexExp()); |
| case EXP.identifier: return visitIdentifier(e.isIdentifierExp()); |
| case EXP.dSymbol: return visitDsymbol(e.isDsymbolExp()); |
| case EXP.this_: return visitThis(e.isThisExp()); |
| case EXP.super_: return visitSuper(e.isSuperExp()); |
| case EXP.null_: return visitNull(e.isNullExp()); |
| case EXP.string_: return visitString(e.isStringExp()); |
| case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp()); |
| case EXP.assocArrayLiteral: return visitAssocArrayLiteral(e.isAssocArrayLiteralExp()); |
| case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp()); |
| case EXP.compoundLiteral: return visitCompoundLiteral(e.isCompoundLiteralExp()); |
| case EXP.type: return visitType(e.isTypeExp()); |
| case EXP.scope_: return visitScope(e.isScopeExp()); |
| case EXP.template_: return visitTemplate(e.isTemplateExp()); |
| case EXP.new_: return visitNew(e.isNewExp()); |
| case EXP.newAnonymousClass: return visitNewAnonClass(e.isNewAnonClassExp()); |
| case EXP.symbolOffset: return visitSymOff(e.isSymOffExp()); |
| case EXP.variable: return visitVar(e.isVarExp()); |
| case EXP.overloadSet: return visitOver(e.isOverExp()); |
| case EXP.tuple: return visitTuple(e.isTupleExp()); |
| case EXP.function_: return visitFunc(e.isFuncExp()); |
| case EXP.declaration: return visitDeclaration(e.isDeclarationExp()); |
| case EXP.typeid_: return visitTypeid(e.isTypeidExp()); |
| case EXP.traits: return visitTraits(e.isTraitsExp()); |
| case EXP.halt: return visitHalt(e.isHaltExp()); |
| case EXP.is_: return visitIs(e.isExp()); |
| case EXP.comma: return visitComma(e.isCommaExp()); |
| case EXP.mixin_: return visitMixin(e.isMixinExp()); |
| case EXP.import_: return visitImport(e.isImportExp()); |
| case EXP.assert_: return visitAssert(e.isAssertExp()); |
| case EXP.throw_: return visitThrow(e.isThrowExp()); |
| case EXP.dotIdentifier: return visitDotId(e.isDotIdExp()); |
| case EXP.dotTemplateDeclaration: return visitDotTemplate(e.isDotTemplateExp()); |
| case EXP.dotVariable: return visitDotVar(e.isDotVarExp()); |
| case EXP.dotTemplateInstance: return visitDotTemplateInstance(e.isDotTemplateInstanceExp()); |
| case EXP.delegate_: return visitDelegate(e.isDelegateExp()); |
| case EXP.dotType: return visitDotType(e.isDotTypeExp()); |
| case EXP.call: return visitCall(e.isCallExp()); |
| case EXP.star: return visitPtr(e.isPtrExp()); |
| case EXP.delete_: return visitDelete(e.isDeleteExp()); |
| case EXP.cast_: return visitCast(e.isCastExp()); |
| case EXP.vector: return visitVector(e.isVectorExp()); |
| case EXP.vectorArray: return visitVectorArray(e.isVectorArrayExp()); |
| case EXP.slice: return visitSlice(e.isSliceExp()); |
| case EXP.arrayLength: return visitArrayLength(e.isArrayLengthExp()); |
| case EXP.interval: return visitInterval(e.isIntervalExp()); |
| case EXP.delegatePointer: return visitDelegatePtr(e.isDelegatePtrExp()); |
| case EXP.delegateFunctionPointer: return visitDelegateFuncptr(e.isDelegateFuncptrExp()); |
| case EXP.array: return visitArray(e.isArrayExp()); |
| case EXP.dot: return visitDot(e.isDotExp()); |
| case EXP.index: return visitIndex(e.isIndexExp()); |
| case EXP.minusMinus: |
| case EXP.plusPlus: return visitPost(e.isPostExp()); |
| case EXP.preMinusMinus: |
| case EXP.prePlusPlus: return visitPre(e.isPreExp()); |
| case EXP.remove: return visitRemove(e.isRemoveExp()); |
| case EXP.question: return visitCond(e.isCondExp()); |
| case EXP.classReference: return visitClassReference(e.isClassReferenceExp()); |
| } |
| } |
| |
| /** |
| * 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 = void; |
| CTFloat.sprint(buffer.ptr, 'g', value); |
| assert(strlen(buffer.ptr) < BUFFER_LEN); |
| if (allowHex) |
| { |
| bool isOutOfRange; |
| real_t r = CTFloat.parse(buffer.ptr, isOutOfRange); |
| //assert(!isOutOfRange); // test/compilable/test22725.c asserts here |
| 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((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-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.writestri
|