diff --git a/.gitmodules b/.gitmodules index d493df0..4e983f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ -[submodule "libdparse"] - path = libdparse - url = https://github.com/dlang-community/libdparse.git [submodule "stdx-allocator"] path = stdx-allocator url = https://github.com/dlang-community/stdx-allocator [submodule "d-test-utils"] path = d-test-utils url = https://github.com/dlang-community/d-test-utils.git +[submodule "dmd"] + path = dmd + url = https://github.com/dlang/dmd.git diff --git a/dmd b/dmd new file mode 160000 index 0000000..b859107 --- /dev/null +++ b/dmd @@ -0,0 +1 @@ +Subproject commit b859107c37a948d0492fccb7a319f0a96b0eecb1 diff --git a/dub.json b/dub.json index a0d6357..c3bfe47 100644 --- a/dub.json +++ b/dub.json @@ -4,17 +4,17 @@ "targetType": "autodetect", "license": "BSL-1.0", "dependencies": { - "libdparse": ">=0.19.2 <1.0.0" + "dmd": "~>2.107.0-beta.1" }, - "targetPath" : "bin/", - "targetName" : "dfmt", - "stringImportPaths" : [ - "bin" + "targetPath": "bin/", + "targetName": "dfmt", + "stringImportPaths": [ + "bin" ], - "versions" : [ + "versions": [ "built_with_dub" ], - "preBuildCommands" : [ - "$DC -run \"$PACKAGE_DIR/dubhash.d\"" + "preBuildCommands": [ + "$DC -run \"$PACKAGE_DIR/dubhash.d\"" ] } diff --git a/libdparse b/libdparse deleted file mode 160000 index fe6d1e3..0000000 --- a/libdparse +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fe6d1e38fb4fc04323170389cfec67ed7fd4e24a diff --git a/makefile b/makefile index d790c0c..2051b1d 100644 --- a/makefile +++ b/makefile @@ -1,8 +1,36 @@ +DMD_ROOT_SRC := \ + $(shell find dmd/compiler/src/dmd/common -name "*.d")\ + $(shell find dmd/compiler/src/dmd/root -name "*.d") +DMD_LEXER_SRC := \ + dmd/compiler/src/dmd/console.d \ + dmd/compiler/src/dmd/entity.d \ + dmd/compiler/src/dmd/errors.d \ + dmd/compiler/src/dmd/errorsink.d \ + dmd/compiler/src/dmd/location.d \ + dmd/compiler/src/dmd/file_manager.d \ + dmd/compiler/src/dmd/globals.d \ + dmd/compiler/src/dmd/id.d \ + dmd/compiler/src/dmd/identifier.d \ + dmd/compiler/src/dmd/lexer.d \ + dmd/compiler/src/dmd/tokens.d \ + dmd/compiler/src/dmd/utils.d \ + $(DMD_ROOT_SRC) + +DMD_PARSER_SRC := \ + dmd/compiler/src/dmd/astbase.d \ + dmd/compiler/src/dmd/parse.d \ + dmd/compiler/src/dmd/parsetimevisitor.d \ + dmd/compiler/src/dmd/transitivevisitor.d \ + dmd/compiler/src/dmd/permissivevisitor.d \ + dmd/compiler/src/dmd/strictvisitor.d \ + dmd/compiler/src/dmd/astenums.d \ + $(DMD_LEXER_SRC) + SRC := $(shell find src -name "*.d") \ - $(shell find libdparse/src -name "*.d") \ - $(shell find stdx-allocator/source -name "*.d") -INCLUDE_PATHS := -Ilibdparse/src -Istdx-allocator/source -Isrc -Jbin -DMD_COMMON_FLAGS := -dip25 -w $(INCLUDE_PATHS) + $(shell find stdx-allocator/source -name "*.d") \ + $(DMD_PARSER_SRC) +INCLUDE_PATHS := -Ilibdparse/src -Istdx-allocator/source -Isrc -Idmd/compiler/src -Jbin -Jdmd +DMD_COMMON_FLAGS := -w $(INCLUDE_PATHS) DMD_DEBUG_FLAGS := -debug -g $(DMD_COMMON_FLAGS) DMD_FLAGS := -O -inline $(DMD_COMMON_FLAGS) DMD_TEST_FLAGS := -unittest -g $(DMD_COMMON_FLAGS) diff --git a/src/dfmt/ast.d b/src/dfmt/ast.d new file mode 100644 index 0000000..eef2c99 --- /dev/null +++ b/src/dfmt/ast.d @@ -0,0 +1,3952 @@ +module dfmt.ast; + +import dmd.visitor; +import dmd.astcodegen; +import dmd.astenums; +import dmd.tokens; +import dmd.parse; +import dmd.mtype; +import dmd.identifier; +import dmd.hdrgen; +import dmd.init; +import dmd.rootobject; +import dmd.target; +import dmd.root.string : toDString; +import dfmt.config; +import dfmt.editorconfig; +import std.range; +import std.format : format; +import std.stdio : File; + +extern (C++) class FormatVisitor : SemanticTimeTransitiveVisitor +{ + File.LockingTextWriter buf; + const Config* config; + string eol; + string tempBuf; // a string buffer to temporarily store data for line splitting + bool useTempBuf; // toggles the buffer being written to + uint depth; // the current indentation level + uint length; // the length of the current line of code + bool declString; // set while declaring alias for string,wstring or dstring + bool isNewline; // used to indent before writing the line + bool insideCase; // true if the node is a child of a CaseStatement + bool insideIfOrDo; // true if the node is a child of an IfStatement or DoStatement + + this(File.LockingTextWriter buf, Config* config) + { + this.buf = buf; + this.config = config; + // Set newline character + final switch (config.end_of_line) + { + case EOL.lf: + eol = "\n"; + break; + case EOL.cr: + eol = "\r"; + break; + case EOL.crlf: + eol = "\r\n"; + break; + case EOL._default: + version (Windows) + { + eol = "\r\n"; + } + else + { + eol = "\n"; + } + break; + case EOL._unspecified: + assert(false, "end of line character is unspecified"); + } + } + + alias visit = SemanticTimeTransitiveVisitor.visit; + + void indent() + { + if (!depth) + return; + auto indent = config.indent_style == IndentStyle.space ? ' '.repeat() + .take(depth * 4) : '\t'.repeat().take(depth); + buf.put(indent.array); + length += indent.length; + } + + void newline() + { + buf.put(eol); + length = 0; + // Indicate that the next write should be indented + isNewline = true; + } + + // Writes a newline only if the data will exceed the maximum allowed line length + bool conditionalNewline() + { + // If the current length crosses the soft limit OR + // if the current length + data length crosses the hard limit, + // insert a newline. + if (length > config.dfmt_soft_max_line_length + || (length + tempBuf.length) > config.max_line_length) + { + newline(); + return true; + } + return false; + } + + void writeTempBuf() + { + useTempBuf = false; + write(tempBuf); + tempBuf = ""; + } + + void write(T)(T data) if (is(T : char) || is(T : dchar)) + { + if (isNewline) + { + indent(); + isNewline = false; + } + if (useTempBuf) + tempBuf ~= data; + else + buf.put(data); + length += 1; + } + + extern (D) void write(T)(T data) if (!(is(T : char) || is(T : dchar))) + { + if (isNewline) + { + indent(); + isNewline = false; + } + if (useTempBuf) + tempBuf ~= data; + else + buf.put(data); + length += data.length; + } + + /*********************************************** + * Helpers to write different AST nodes to buffer + */ + void writeStc(StorageClass stc) + { + bool writeSpace = false; + + if (stc & ASTCodegen.STC.scopeinferred) + { + stc &= ~(ASTCodegen.STC.scope_ | ASTCodegen.STC.scopeinferred); + } + + if (stc & ASTCodegen.STC.returninferred) + { + stc &= ~(ASTCodegen.STC.return_ | ASTCodegen.STC.returninferred); + } + + // Put scope ref return into a standard order + string rrs; + const isout = (stc & ASTCodegen.STC.out_) != 0; + 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: + write(rrs); + writeSpace = true; + stc &= ~( + ASTCodegen.STC.out_ | ASTCodegen.STC.scope_ + | ASTCodegen.STC.ref_ | ASTCodegen.STC.return_); + break; + } + + while (stc) + { + const s = stcToString(stc); + if (!s.length) + break; + if (writeSpace) + write(' '); + writeSpace = true; + write(s); + } + + if (writeSpace) + write(' '); + + } + + void writeFloat(Type type, real value) + { + import dmd.root.ctfloat; + import core.stdc.string : strlen; + + /* 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, BUFFER_LEN, 'g', value); + assert(strlen(buffer.ptr) < BUFFER_LEN); + if (buffer.ptr[strlen(buffer.ptr) - 1] == '.') + buffer.ptr[strlen(buffer.ptr) - 1] = char.init; + write(buffer.array); + + if (type) + { + Type t = type.toBasetype(); + switch (t.ty) + { + case Tfloat32: + case Timaginary32: + case Tcomplex32: + write('F'); + break; + case Tfloat80: + case Timaginary80: + case Tcomplex80: + write('L'); + break; + default: + break; + } + if (t.isimaginary()) + write('i'); + } + } + + void writeExpr(ASTCodegen.Expression e) + { + import dmd.hdrgen : EXPtoString; + + void visit(ASTCodegen.Expression e) + { + write(EXPtoString(e.op)); + } + + void visitInteger(ASTCodegen.IntegerExp e) + { + auto 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) + { + foreach (em; *sym.members) + { + if ((cast(ASTCodegen.EnumMember) em).value.toInteger == v) + { + write(format("%s.%s", sym.toString(), em.ident.toString())); + return; + } + } + } + + write(format("cast(%s)", te.sym.toString())); + t = te.sym.memtype; + goto L1; + } + case Tchar: + case Twchar: + case Tdchar: + { + write(cast(dchar) v); + break; + } + case Tint8: + write("cast(byte)"); + goto L2; + case Tint16: + write("cast(short)"); + goto L2; + case Tint32: + L2: + write(format("%d", cast(int) v)); + break; + case Tuns8: + write("cast(ubyte)"); + goto case Tuns32; + case Tuns16: + write("cast(ushort)"); + goto case Tuns32; + case Tuns32: + write(format("%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 + write("cast(long)-9223372036854775808"); + } + else + { + write(format("%uL", v)); + } + break; + case Tuns64: + write(format("%uLU", v)); + break; + case Tbool: + write(v ? "true" : "false"); + break; + case Tpointer: + write("cast("); + write(t.toString()); + write(')'); + if (target.ptrsize == 8) + goto case Tuns64; + else if (target.ptrsize == 4 || target.ptrsize == 2) + goto case Tuns32; + else + assert(0); + + case Tvoid: + write("cast(void)0"); + break; + default: + break; + } + } + else if (v & 0x8000000000000000L) + write(format("0x%u", v)); + else + write(format("%u", v)); + } + + void visitError(ASTCodegen.ErrorExp _) + { + write("__error"); + } + + void visitVoidInit(ASTCodegen.VoidInitExp _) + { + write("void"); + } + + void visitReal(ASTCodegen.RealExp e) + { + writeFloat(e.type, e.value); + } + + void visitComplex(ASTCodegen.ComplexExp e) + { + /* Print as: + * (re+imi) + */ + write('('); + writeFloat(e.type, e.value.re); + write('+'); + writeFloat(e.type, e.value.im); + write("i)"); + } + + void visitIdentifier(ASTCodegen.IdentifierExp e) + { + write(e.ident.toString()); + } + + void visitDsymbol(ASTCodegen.DsymbolExp e) + { + write(e.s.toString()); + } + + void visitThis(ASTCodegen.ThisExp _) + { + write("this"); + } + + void visitSuper(ASTCodegen.SuperExp _) + { + write("super"); + } + + void visitNull(ASTCodegen.NullExp _) + { + write("null"); + } + + void visitString(ASTCodegen.StringExp e) + { + write('"'); + foreach (i; 0 .. e.len) + { + write(e.getCodeUnit(i)); + } + write('"'); + if (e.postfix) + write(e.postfix); + } + + void visitArrayLiteral(ASTCodegen.ArrayLiteralExp e) + { + write('['); + writeArgs(e.elements, e.basis); + write(']'); + } + + void visitAssocArrayLiteral(ASTCodegen.AssocArrayLiteralExp e) + { + write('['); + foreach (i, key; *e.keys) + { + if (i) + write(", "); + writeExprWithPrecedence(key, PREC.assign); + if (config.dfmt_space_before_aa_colon) + write(' '); + write(": "); + auto value = (*e.values)[i]; + writeExprWithPrecedence(value, PREC.assign); + } + write(']'); + } + + void visitStructLiteral(ASTCodegen.StructLiteralExp e) + { + import dmd.expression; + + write(e.sd.toString()); + write('('); + // 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) + write(""); + else + { + const old = e.stageflags; + e.stageflags |= stageToCBuffer; + writeArgs(e.elements); + e.stageflags = old; + } + write(')'); + } + + void visitCompoundLiteral(ASTCodegen.CompoundLiteralExp e) + { + write('('); + writeTypeWithIdent(e.type, null); + write(')'); + writeInitializer(e.initializer); + } + + void visitType(ASTCodegen.TypeExp e) + { + writeTypeWithIdent(e.type, null); + } + + void visitScope(ASTCodegen.ScopeExp e) + { + import core.stdc.string : strlen; + + if (e.sds.isTemplateInstance()) + { + e.sds.accept(this); + } + else + { + write(e.sds.kind().toDString()); + write(' '); + write(e.sds.toString()); + } + } + + void visitTemplate(ASTCodegen.TemplateExp e) + { + write(e.td.toString()); + } + + void visitNew(ASTCodegen.NewExp e) + { + if (e.thisexp) + { + writeExprWithPrecedence(e.thisexp, PREC.primary); + write('.'); + } + write("new "); + writeTypeWithIdent(e.newtype, null); + if (e.arguments && e.arguments.length) + { + write('('); + writeArgs(e.arguments, null, e.names); + write(')'); + } + } + + void visitNewAnonClass(ASTCodegen.NewAnonClassExp e) + { + if (e.thisexp) + { + writeExprWithPrecedence(e.thisexp, PREC.primary); + write('.'); + } + write("new"); + write(" class "); + if (e.arguments && e.arguments.length) + { + write('('); + writeArgs(e.arguments); + write(')'); + } + if (e.cd) + e.cd.accept(this); + } + + void visitSymOff(ASTCodegen.SymOffExp e) + { + if (e.offset) + write(format("(& %s%+lld)", e.var.toString(), e.offset)); + else if (e.var.isTypeInfoDeclaration()) + write(e.var.toString()); + else + write(format("& %s", e.var.toString())); + } + + void visitVar(ASTCodegen.VarExp e) + { + write(e.var.toString()); + } + + void visitOver(ASTCodegen.OverExp e) + { + write(e.vars.ident.toString()); + } + + void visitTuple(ASTCodegen.TupleExp e) + { + if (e.e0) + { + write('('); + writeExpr(e.e0); + write(", AliasSeq!("); + writeArgs(e.exps); + write("))"); + } + else + { + write("AliasSeq!("); + writeArgs(e.exps); + write(')'); + } + } + + void visitFunc(ASTCodegen.FuncExp e) + { + e.fd.accept(this); + } + + void visitDeclaration(ASTCodegen.DeclarationExp e) + { + /* Normal dmd execution won't reach here - regular variable declarations + * are handled in visit(ASTCodegen.ExpStatement), so here would be used only when + * we'll directly call Expression.toString() 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. + write('('); + + writeVarDecl(var, false); + + write(';'); + write(')'); + } + else + e.declaration.accept(this); + } + } + + void visitTypeid(ASTCodegen.TypeidExp e) + { + write("typeid("); + writeObject(e.obj); + write(')'); + } + + void visitTraits(ASTCodegen.TraitsExp e) + { + write("__traits("); + if (e.ident) + write(e.ident.toString()); + if (e.args) + { + foreach (arg; *e.args) + { + write(", "); + writeObject(arg); + } + } + write(')'); + } + + void visitHalt(ASTCodegen.HaltExp _) + { + write("halt"); + } + + void visitIs(ASTCodegen.IsExp e) + { + write("is("); + writeTypeWithIdent(e.targ, e.id); + if (e.tok2 != TOK.reserved) + { + write(format(" %s %s", Token.toChars(e.tok), Token.toChars(e.tok2))); + } + else if (e.tspec) + { + if (e.tok == TOK.colon) + write(" : "); + else + write(" == "); + writeTypeWithIdent(e.tspec, null); + } + if (e.parameters && e.parameters.length) + { + write(", "); + visitTemplateParameters(e.parameters); + } + write(')'); + } + + void visitUna(ASTCodegen.UnaExp e) + { + write(EXPtoString(e.op)); + writeExprWithPrecedence(e.e1, precedence[e.op]); + } + + void visitLoweredAssignExp(ASTCodegen.LoweredAssignExp e) + { + visit(cast(ASTCodegen.BinExp) e); + } + + static bool[EXP] operators = [ + EXP.lessThan: true, EXP.greaterThan: true, EXP.lessOrEqual: true, + EXP.greaterOrEqual: true, EXP.equal: true, EXP.notEqual: true, + EXP.identity: true, EXP.notIdentity: true, EXP.index: true, + EXP.is_: true, EXP.leftShift: true, EXP.rightShift: true, + EXP.leftShiftAssign: true, EXP.rightShiftAssign: true, + EXP.unsignedRightShift: true, EXP.unsignedRightShiftAssign: true, + EXP.concatenate: true, EXP.concatenateAssign: true, // ~= + EXP.concatenateElemAssign: true, + EXP.concatenateDcharAssign: true, EXP.add: true, EXP.min: true, + EXP.addAssign: true, EXP.minAssign: true, EXP.mul: true, EXP.div: true, + EXP.mod: true, EXP.mulAssign: true, EXP.divAssign: true, + EXP.modAssign: true, EXP.and: true, EXP.or: true, EXP.xor: true, + EXP.andAssign: true, EXP.orAssign: true, EXP.xorAssign: true, + EXP.assign: true, EXP.not: true, EXP.tilde: true, EXP.plusPlus: true, + EXP.minusMinus: true, EXP.construct: true, EXP.blit: true, + EXP.dot: true, EXP.comma: true, EXP.question: true, EXP.andAnd: true, + EXP.orOr: true, EXP.prePlusPlus: true, EXP.preMinusMinus: true, + ]; + + void visitBin(ASTCodegen.BinExp e) + { + writeExprWithPrecedence(e.e1, precedence[e.op]); + if (!(!config.dfmt_split_operator_at_line_end && e.op in operators && conditionalNewline())) + write(' '); + write(EXPtoString(e.op)); + if (!(config.dfmt_split_operator_at_line_end && e.op in operators && conditionalNewline())) + write(' '); + writeExprWithPrecedence(e.e2, cast(PREC)(precedence[e.op] + 1)); + } + + void visitComma(ASTCodegen.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(ASTCodegen.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()) + { + ASTCodegen.Expression commaExtract; + auto exp = ei.exp; + if (auto ce = exp.isConstructExp()) + commaExtract = ce.e2; + else if (auto se = exp.isStructLiteralExp()) + commaExtract = se; + + if (commaExtract) + { + writeExprWithPrecedence(commaExtract, precedence[exp.op]); + return; + } + } + + // not one of the known cases, go on the old path + visitBin(cast(ASTCodegen.BinExp) e); + return; + } + + void visitMixin(ASTCodegen.MixinExp e) + { + write("mixin("); + writeArgs(e.exps); + write(')'); + } + + void visitImport(ASTCodegen.ImportExp e) + { + write("import("); + writeExprWithPrecedence(e.e1, PREC.assign); + write(')'); + } + + void visitAssert(ASTCodegen.AssertExp e) + { + write("assert("); + writeExprWithPrecedence(e.e1, PREC.assign); + if (e.msg) + { + write(", "); + writeExprWithPrecedence(e.msg, PREC.assign); + } + write(')'); + } + + void visitThrow(ASTCodegen.ThrowExp e) + { + write("throw "); + writeExprWithPrecedence(e.e1, PREC.unary); + } + + void visitDotId(ASTCodegen.DotIdExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + if (e.arrow) + write("->"); + else + write('.'); + write(e.ident.toString()); + } + + void visitDotTemplate(ASTCodegen.DotTemplateExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('.'); + write(e.td.toString()); + } + + void visitDotVar(ASTCodegen.DotVarExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('.'); + write(e.var.toString()); + } + + void visitDotTemplateInstance(ASTCodegen.DotTemplateInstanceExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('.'); + e.ti.accept(this); + } + + void visitDelegate(ASTCodegen.DelegateExp e) + { + write('&'); + if (!e.func.isNested() || e.func.needThis()) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('.'); + } + write(e.func.toString()); + } + + void visitDotType(ASTCodegen.DotTypeExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('.'); + write(e.sym.toString()); + } + + void visitCall(ASTCodegen.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 + */ + writeExpr(e.e1); + } + writeExprWithPrecedence(e.e1, precedence[e.op]); + write('('); + writeArgs(e.arguments, null, e.names); + write(')'); + } + + void visitPtr(ASTCodegen.PtrExp e) + { + write('*'); + writeExprWithPrecedence(e.e1, precedence[e.op]); + } + + void visitDelete(ASTCodegen.DeleteExp e) + { + write("delete "); + writeExprWithPrecedence(e.e1, precedence[e.op]); + } + + void visitCast(ASTCodegen.CastExp e) + { + write("cast("); + if (e.to) + writeTypeWithIdent(e.to, null); + else + { + write(MODtoString(e.mod)); + } + write(')'); + if (config.dfmt_space_after_cast) + { + write(' '); + } + writeExprWithPrecedence(e.e1, precedence[e.op]); + } + + void visitVector(ASTCodegen.VectorExp e) + { + write("cast("); + writeTypeWithIdent(e.to, null); + write(')'); + writeExprWithPrecedence(e.e1, precedence[e.op]); + } + + void visitVectorArray(ASTCodegen.VectorArrayExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write(".array"); + } + + void visitSlice(ASTCodegen.SliceExp e) + { + writeExprWithPrecedence(e.e1, precedence[e.op]); + write('['); + if (e.upr || e.lwr) + { + if (e.lwr) + writeSize(e.lwr); + else + write('0'); + write(".."); + if (e.upr) + writeSize(e.upr); + else + write('$'); + } + write(']'); + } + + void visitArrayLength(ASTCodegen.ArrayLengthExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write(".length"); + } + + void visitInterval(ASTCodegen.IntervalExp e) + { + writeExprWithPrecedence(e.lwr, PREC.assign); + write(".."); + writeExprWithPrecedence(e.upr, PREC.assign); + } + + void visitDelegatePtr(ASTCodegen.DelegatePtrExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write(".ptr"); + } + + void visitDelegateFuncptr(ASTCodegen.DelegateFuncptrExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write(".funcptr"); + } + + void visitArray(ASTCodegen.ArrayExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('['); + writeArgs(e.arguments); + write(']'); + } + + void visitDot(ASTCodegen.DotExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('.'); + writeExprWithPrecedence(e.e2, PREC.primary); + } + + void visitIndex(ASTCodegen.IndexExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write('['); + writeSize(e.e2); + write(']'); + } + + void visitPost(ASTCodegen.PostExp e) + { + writeExprWithPrecedence(e.e1, precedence[e.op]); + write(EXPtoString(e.op)); + } + + void visitPre(ASTCodegen.PreExp e) + { + write(EXPtoString(e.op)); + writeExprWithPrecedence(e.e1, precedence[e.op]); + } + + void visitRemove(ASTCodegen.RemoveExp e) + { + writeExprWithPrecedence(e.e1, PREC.primary); + write(".remove("); + writeExprWithPrecedence(e.e2, PREC.assign); + write(')'); + } + + void visitCond(ASTCodegen.CondExp e) + { + writeExprWithPrecedence(e.econd, PREC.oror); + write(" ? "); + writeExprWithPrecedence(e.e1, PREC.expr); + write(" : "); + writeExprWithPrecedence(e.e2, PREC.cond); + } + + void visitDefaultInit(ASTCodegen.DefaultInitExp e) + { + write(EXPtoString(e.op)); + } + + void visitClassReference(ASTCodegen.ClassReferenceExp e) + { + write(e.value.toString()); + } + + 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()); + case EXP.loweredAssignExp: + return visitLoweredAssignExp(e.isLoweredAssignExp()); + } + } + + void writeArgs(ASTCodegen.Expressions* expressions, + ASTCodegen.Expression basis = null, ASTCodegen.Identifiers* names = null) + { + if (!expressions || !expressions.length) + return; + version (all) + { + foreach (i, el; *expressions) + { + if (i) + write(", "); + + if (names && i < names.length && (*names)[i]) + { + write((*names)[i].toString()); + if (config.dfmt_space_before_named_arg_colon) + write(' '); + write(": "); + } + if (!el) + el = basis; + if (el) + writeExprWithPrecedence(el, PREC.assign); + } + } + else + { + if (basis) + { + write("0.."); + buf.print(expressions.length); + write(": "); + writeExprWithPrecedence(basis, PREC.assign); + } + foreach (i, el; *expressions) + { + if (el) + { + if (basis) + { + write(", "); + write(i); + write(": "); + } + else if (i) + write(", "); + writeExprWithPrecedence(el, PREC.assign); + } + } + } + } + + void writeExprWithPrecedence(ASTCodegen.Expression e, PREC pr) + { + if (e.op == 0xFF) + { + write(""); + return; + } + assert(precedence[e.op] != PREC.zero); + assert(pr != PREC.zero); + /* Despite precedence, we don't allow a= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel)) + { + write('('); + writeExpr(e); + write(')'); + } + else + { + writeExpr(e); + } + } + + void writeTypeWithIdent(ASTCodegen.Type t, const Identifier ident, ubyte modMask = 0) + { + if (auto tf = t.isTypeFunction()) + { + writeFuncIdentWithPrefix(tf, ident, null); + return; + } + writeWithMask(t, modMask); + if (ident) + { + write(' '); + write(ident.toString()); + } + } + + void writeWithMask(Type t, ubyte modMask) + { + // Tuples and functions don't use the type constructor syntax + if (modMask == t.mod || t.ty == Tfunction || t.ty == Ttuple) + { + writeType(t); + } + else + { + ubyte m = t.mod & ~(t.mod & modMask); + if (m & MODFlags.shared_) + { + write(MODtoString(MODFlags.shared_)); + write('('); + } + if (m & MODFlags.wild) + { + write(MODtoString(MODFlags.wild)); + write('('); + } + if (m & (MODFlags.const_ | MODFlags.immutable_)) + { + write(MODtoString(m & (MODFlags.const_ | MODFlags.immutable_))); + write('('); + } + writeType(t); + if (m & (MODFlags.const_ | MODFlags.immutable_)) + write(')'); + if (m & MODFlags.wild) + write(')'); + if (m & MODFlags.shared_) + write(')'); + } + } + + void writeStatement(ASTCodegen.Statement s) + { + void visitDefaultCase(ASTCodegen.Statement _) + { + assert(0, "unrecognized statement in writeStatement()"); + } + + void visitError(ASTCodegen.ErrorStatement _) + { + write("__error__"); + newline(); + } + + void visitExp(ASTCodegen.ExpStatement s) + { + if (s.exp && s.exp.op == EXP.declaration + && (cast(ASTCodegen.DeclarationExp) s.exp).declaration) + { + (cast(ASTCodegen.DeclarationExp) s.exp).declaration.accept(this); + return; + } + if (s.exp) + writeExpr(s.exp); + write(';'); + newline(); + } + + void visitDtorExp(ASTCodegen.DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(ASTCodegen.MixinStatement s) + { + write("mixin("); + writeArgs(s.exps); + write(");"); + } + + void visitCompound(ASTCodegen.CompoundStatement s) + { + foreach (sx; *s.statements) + { + if (sx) + { + writeStatement(sx); + } + } + } + + void visitCompoundAsm(ASTCodegen.CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitCompoundDeclaration(ASTCodegen.CompoundDeclarationStatement s) + { + bool anywritten = false; + foreach (sx; *s.statements) + { + auto ds = sx ? sx.isExpStatement() : null; + if (ds && ds.exp.isDeclarationExp()) + { + auto d = ds.exp.isDeclarationExp().declaration; + if (auto v = d.isVarDeclaration()) + { + writeVarDecl(v, anywritten); + } + else + d.accept(this); + anywritten = true; + } + } + write(';'); + } + + void visitUnrolledLoop(ASTCodegen.UnrolledLoopStatement s) + { + write("/*unrolled*/ {"); + newline(); + depth++; + foreach (sx; *s.statements) + { + if (sx) + writeStatement(sx); + } + depth--; + write('}'); + newline(); + } + + void visitScope(ASTCodegen.ScopeStatement s) + { + if (!insideCase) + { + if (config.dfmt_brace_style != BraceStyle.knr + || s.statement.isCompoundStatement.statements.length != 1) + write('{'); + newline(); + } + depth++; + if (s.statement) + writeStatement(s.statement); + depth--; + if (!insideCase) + { + if (config.dfmt_brace_style != BraceStyle.knr + || s.statement.isCompoundStatement.statements.length != 1) + { + write('}'); + if (!insideIfOrDo) + newline(); + } + } + } + + void visitWhile(ASTCodegen.WhileStatement s) + { + write("while"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (auto p = s.param) + { + // Print condition assignment + StorageClass stc = p.storageClass; + if (!p.type && !stc) + stc = STC.auto_; + writeStc(stc); + if (p.type) + writeTypeWithIdent(p.type, p.ident); + else + write(p.ident.toString()); + write(" = "); + } + writeExpr(s.condition); + write(')'); + newline(); + if (s._body) + writeStatement(s._body); + } + + void visitDo(ASTCodegen.DoStatement s) + { + write("do"); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + if (s._body.isScopeStatement) + { + insideIfOrDo = true; + writeStatement(s._body); + insideIfOrDo = false; + } + else + { + depth++; + writeStatement(s._body); + depth--; + } + if (config.dfmt_brace_style == BraceStyle.otbs) + write(' '); + else if (config.dfmt_brace_style != BraceStyle.knr + || s._body.isScopeStatement.statement.isCompoundStatement.statements.length > 1) + newline(); + write("while"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + writeExpr(s.condition); + write(");"); + newline(); + } + + void visitFor(ASTCodegen.ForStatement s) + { + write("for"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (s._init) + { + writeStatement(s._init); + } + else + write(';'); + if (s.condition) + { + write(' '); + writeExpr(s.condition); + } + write(';'); + if (s.increment) + { + write(' '); + writeExpr(s.increment); + } + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + if (s._body) + writeStatement(s._body); + depth--; + write('}'); + newline(); + } + + void visitForeachWithoutBody(ASTCodegen.ForeachStatement s) + { + write(Token.toString(s.op)); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + foreach (i, p; *s.parameters) + { + if (i) + write(", "); + writeStc(p.storageClass); + if (p.type) + writeTypeWithIdent(p.type, p.ident); + else + write(p.ident.toString()); + } + write("; "); + writeExpr(s.aggr); + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + } + + void visitForeach(ASTCodegen.ForeachStatement s) + { + visitForeachWithoutBody(s); + write('{'); + newline(); + depth++; + if (s._body) + writeStatement(s._body); + depth--; + write('}'); + newline(); + } + + void visitForeachRangeWithoutBody(ASTCodegen.ForeachRangeStatement s) + { + write(Token.toString(s.op)); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (s.prm.type) + writeTypeWithIdent(s.prm.type, s.prm.ident); + else + write(s.prm.ident.toString()); + write("; "); + writeExpr(s.lwr); + write(" .. "); + writeExpr(s.upr); + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + } + + void visitForeachRange(ASTCodegen.ForeachRangeStatement s) + { + visitForeachRangeWithoutBody(s); + write('{'); + newline(); + depth++; + if (s._body) + writeStatement(s._body); + depth--; + write('}'); + newline(); + } + + void visitStaticForeach(ASTCodegen.StaticForeachStatement s) + { + indent(); + write("static "); + if (s.sfe.aggrfe) + { + visitForeach(s.sfe.aggrfe); + } + else + { + assert(s.sfe.rangefe); + visitForeachRange(s.sfe.rangefe); + } + } + + void visitForwarding(ASTCodegen.ForwardingStatement s) + { + writeStatement(s.statement); + } + + void visitIf(ASTCodegen.IfStatement s) + { + write("if"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (Parameter p = s.prm) + { + StorageClass stc = p.storageClass; + if (!p.type && !stc) + stc = STC.auto_; + writeStc(stc); + if (p.type) + writeTypeWithIdent(p.type, p.ident); + else + write(p.ident.toString()); + write(" = "); + } + writeExpr(s.condition); + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + if (s.ifbody.isScopeStatement()) + { + insideIfOrDo = true; + writeStatement(s.ifbody); + insideIfOrDo = false; + } + else + { + depth++; + writeStatement(s.ifbody); + depth--; + } + if (s.elsebody) + { + if (config.dfmt_brace_style == BraceStyle.otbs) + write(' '); + else if (config.dfmt_brace_style != BraceStyle.knr + || s.ifbody.isScopeStatement.statement.isCompoundStatement.statements.length + > 1) + newline(); + write("else"); + if (!s.elsebody.isIfStatement() && config.dfmt_brace_style == BraceStyle.allman) + { + newline(); + } + else + { + write(' '); + } + if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement()) + { + writeStatement(s.elsebody); + } + else + { + depth++; + writeStatement(s.elsebody); + depth--; + } + } + else + { + newline(); + } + } + + void visitConditional(ASTCodegen.ConditionalStatement s) + { + s.condition.accept(this); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + if (s.ifbody) + writeStatement(s.ifbody); + depth--; + write('}'); + newline(); + if (s.elsebody) + { + write("else"); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + depth++; + newline(); + writeStatement(s.elsebody); + depth--; + write('}'); + newline(); + } + } + + void visitPragma(ASTCodegen.PragmaStatement s) + { + write("pragma"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + write(s.ident.toString()); + if (s.args && s.args.length) + { + write(", "); + writeArgs(s.args); + } + write(')'); + if (s._body) + { + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + writeStatement(s._body); + depth--; + write('}'); + newline(); + } + else + { + write(';'); + newline(); + } + } + + void visitStaticAssert(ASTCodegen.StaticAssertStatement s) + { + s.sa.accept(this); + } + + void visitSwitch(ASTCodegen.SwitchStatement s) + { + write(s.isFinal ? "final switch" : "switch"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (auto p = s.param) + { + // Print condition assignment + StorageClass stc = p.storageClass; + if (!p.type && !stc) + stc = STC.auto_; + writeStc(stc); + if (p.type) + writeTypeWithIdent(p.type, p.ident); + else + write(p.ident.toString()); + write(" = "); + } + writeExpr(s.condition); + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + if (s._body) + { + if (!s._body.isScopeStatement()) + { + write('{'); + newline(); + depth++; + writeStatement(s._body); + depth--; + write('}'); + newline(); + } + else + { + writeStatement(s._body); + } + } + } + + void visitCase(ASTCodegen.CaseStatement s) + { + if (config.dfmt_align_switch_statements) + depth--; + write("case "); + writeExpr(s.exp); + write(':'); + newline(); + insideCase = true; + writeStatement(s.statement); + insideCase = false; + if (config.dfmt_align_switch_statements) + depth++; + } + + void visitCaseRange(ASTCodegen.CaseRangeStatement s) + { + write("case "); + writeExpr(s.first); + write(": .. case "); + writeExpr(s.last); + write(':'); + newline(); + writeStatement(s.statement); + } + + void visitDefault(ASTCodegen.DefaultStatement s) + { + if (config.dfmt_align_switch_statements) + depth--; + write("default:"); + newline(); + writeStatement(s.statement); + if (config.dfmt_align_switch_statements) + depth++; + } + + void visitGotoDefault(ASTCodegen.GotoDefaultStatement _) + { + write("goto default;"); + newline(); + } + + void visitGotoCase(ASTCodegen.GotoCaseStatement s) + { + write("goto case"); + if (s.exp) + { + write(' '); + writeExpr(s.exp); + } + write(';'); + newline(); + } + + void visitReturn(ASTCodegen.ReturnStatement s) + { + write("return "); + if (s.exp) + writeExpr(s.exp); + write(';'); + newline(); + } + + void visitBreak(ASTCodegen.BreakStatement s) + { + write("break"); + if (s.ident) + { + write(' '); + write(s.ident.toString()); + } + write(';'); + newline(); + } + + void visitContinue(ASTCodegen.ContinueStatement s) + { + write("continue"); + if (s.ident) + { + write(' '); + write(s.ident.toString()); + } + write(';'); + newline(); + } + + void visitSynchronized(ASTCodegen.SynchronizedStatement s) + { + write("synchronized"); + if (s.exp) + { + write('('); + writeExpr(s.exp); + write(')'); + } + if (s._body) + { + write(' '); + writeStatement(s._body); + } + } + + void visitWith(ASTCodegen.WithStatement s) + { + write("with"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + writeExpr(s.exp); + write(")"); + newline(); + if (s._body) + writeStatement(s._body); + } + + void visitTryCatch(ASTCodegen.TryCatchStatement s) + { + write("try"); + newline(); + if (s._body) + { + if (s._body.isScopeStatement()) + { + writeStatement(s._body); + } + else + { + depth++; + writeStatement(s._body); + depth--; + } + } + foreach (c; *s.catches) + { + write("catch"); + if (c.type) + { + write('('); + writeTypeWithIdent(c.type, c.ident); + write(')'); + } + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + if (c.handler) + writeStatement(c.handler); + depth--; + write('}'); + newline(); + } + } + + void visitTryFinally(ASTCodegen.TryFinallyStatement s) + { + write("try"); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + writeStatement(s._body); + depth--; + write('}'); + newline(); + write("finally"); + newline(); + if (s.finalbody.isScopeStatement()) + { + writeStatement(s.finalbody); + } + else + { + depth++; + writeStatement(s.finalbody); + depth--; + } + } + + void visitScopeGuard(ASTCodegen.ScopeGuardStatement s) + { + write(Token.toString(s.tok)); + write(' '); + if (s.statement) + writeStatement(s.statement); + } + + void visitThrow(ASTCodegen.ThrowStatement s) + { + write("throw "); + writeExpr(s.exp); + write(';'); + newline(); + } + + void visitDebug(ASTCodegen.DebugStatement s) + { + if (s.statement) + { + writeStatement(s.statement); + } + } + + void visitGoto(ASTCodegen.GotoStatement s) + { + write("goto "); + write(s.ident.toString()); + write(';'); + newline(); + } + + void visitLabel(ASTCodegen.LabelStatement s) + { + write(s.ident.toString()); + write(':'); + if (config.dfmt_compact_labeled_statements) + write(' '); + else + newline(); + if (s.statement) + writeStatement(s.statement); + } + + void visitAsm(ASTCodegen.AsmStatement s) + { + write("asm { "); + Token* t = s.tokens; + depth++; + while (t) + { + write(Token.toString(t.value)); + 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) + { + write(' '); + } + t = t.next; + } + depth--; + write("; }"); + newline(); + } + + void visitInlineAsm(ASTCodegen.InlineAsmStatement s) + { + visitAsm(s); + } + + void visitGccAsm(ASTCodegen.GccAsmStatement s) + { + visitAsm(s); + } + + void visitImport(ASTCodegen.ImportStatement s) + { + foreach (imp; *s.imports) + { + imp.accept(this); + } + } + + import dmd.statement; + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + } + + void writeFuncBody(ASTCodegen.FuncDeclaration f) + { + if (config.dfmt_brace_style == BraceStyle.allman || config.dfmt_brace_style + == BraceStyle.knr) + newline(); + else + write(' '); + writeContracts(f); + write('{'); + newline(); + depth++; + if (f.fbody) + { + writeStatement(f.fbody); + } + else + { + write('{'); + newline(); + write('}'); + newline(); + } + depth--; + write('}'); + newline(); + } + + void writeContracts(ASTCodegen.FuncDeclaration f) + { + bool requireDo = false; + // in{} + if (f.frequires) + { + foreach (frequire; *f.frequires) + { + write("in"); + if (auto es = frequire.isExpStatement()) + { + assert(es.exp && es.exp.op == EXP.assert_); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + writeExpr((cast(ASTCodegen.AssertExp) es.exp).e1); + write(')'); + newline(); + requireDo = false; + } + else + { + newline(); + writeStatement(frequire); + requireDo = true; + } + } + } + // out{} + if (f.fensures) + { + foreach (fensure; *f.fensures) + { + write("out"); + if (auto es = fensure.ensure.isExpStatement()) + { + assert(es.exp && es.exp.op == EXP.assert_); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (fensure.id) + { + write(fensure.id.toString()); + } + write("; "); + writeExpr((cast(ASTCodegen.AssertExp) es.exp).e1); + write(')'); + newline(); + requireDo = false; + } + else + { + if (fensure.id) + { + write('('); + write(fensure.id.toString()); + write(')'); + } + newline(); + writeStatement(fensure.ensure); + requireDo = true; + } + } + } + + if (requireDo) + { + write("do"); + if (config.dfmt_brace_style == BraceStyle.allman + || config.dfmt_brace_style == BraceStyle.knr) + newline(); + else + write(' '); + } + } + + void writeInitializer(Initializer inx) + { + void visitError(ErrorInitializer _) + { + write("__error__"); + } + + void visitVoid(VoidInitializer _) + { + write("void"); + } + + void visitStruct(StructInitializer si) + { + write('{'); + foreach (i, const id; si.field) + { + if (i) + write(", "); + if (id) + { + write(id.toString()); + write(':'); + } + if (auto iz = si.value[i]) + writeInitializer(iz); + } + write('}'); + } + + void visitArray(ArrayInitializer ai) + { + write('['); + foreach (i, ex; ai.index) + { + if (i) + write(", "); + if (ex) + { + writeExpr(ex); + if (config.dfmt_space_before_aa_colon) + write(' '); + write(": "); + } + if (auto iz = ai.value[i]) + writeInitializer(iz); + } + write(']'); + } + + void visitExp(ExpInitializer ei) + { + writeExpr(ei.exp); + } + + void visitC(CInitializer ci) + { + write('{'); + foreach (i, ref DesigInit di; ci.initializerList) + { + if (i) + write(", "); + if (di.designatorList) + { + foreach (ref Designator d; (*di.designatorList)[]) + { + if (d.exp) + { + write('['); + d.exp.accept(this); + write(']'); + } + else + { + write('.'); + write(d.ident.toString()); + } + } + write('='); + } + writeInitializer(di.initializer); + } + write('}'); + } + + void visitDefault(DefaultInitializer di) + { + write("{ }"); + } + + mixin VisitInitializer!void visit; + visit.VisitInitializer(inx); + } + + void writeObject(RootObject oarg) + { + /* 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. + */ + import dmd.dtemplate; + import dmd.expression : WANTvalue; + + if (auto t = isType(oarg)) + { + writeTypeWithIdent(t, null); + } + else if (auto e = isExpression(oarg)) + { + writeExprWithPrecedence(e, PREC.assign); + } + else if (ASTCodegen.Dsymbol s = isDsymbol(oarg)) + { + const p = s.ident ? s.ident.toString() : s.toString(); + write(p); + } + else if (auto v = isTuple(oarg)) + { + auto args = &v.objects; + foreach (i, arg; *args) + { + if (i) + write(", "); + writeObject(arg); + } + } + else if (auto p = isParameter(oarg)) + { + writeParam(p); + } + else if (!oarg) + { + write("NULL"); + } + else + { + assert(0); + } + } + + void writeVarDecl(ASTCodegen.VarDeclaration v, bool anywritten) + { + void vinit(ASTCodegen.VarDeclaration v) + { + auto ie = v._init.isExpInitializer(); + if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit)) + writeExpr((cast(ASTCodegen.AssignExp) ie.exp).e2); + else + writeInitializer(v._init); + } + + if (anywritten) + { + write(", "); + write(v.ident.toString()); + } + else + { + auto stc = v.storage_class; + writeStc(stc); + if (v.type) + writeTypeWithIdent(v.type, v.ident); + else + write(v.ident.toString()); + } + if (v._init) + { + write(" = "); + vinit(v); + } + } + + void writeSize(ASTCodegen.Expression e) + { + import dmd.expression : WANTvalue; + + if (e.type == Type.tsize_t) + { + ASTCodegen.Expression ex = (e.op == EXP.cast_ ? (cast(ASTCodegen.CastExp) e).e1 : e); + const ulong uval = ex.op == EXP.int64 ? ex.toInteger() : cast(ulong)-1; + if (cast(long) uval >= 0) + { + ulong 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) + { + write(format("%lu", uval)); + return; + } + } + } + writeExprWithPrecedence(e, PREC.assign); + } + + void writeFuncIdentWithPrefix(TypeFunction t, const Identifier ident, + ASTCodegen.TemplateDeclaration td) + { + if (t.inuse) + { + t.inuse = 2; // flag error to caller + return; + } + t.inuse++; + + /* Use 'storage class' (prefix) style for attributes + */ + if (t.mod) + { + write(MODtoString(t.mod)); + write(' '); + } + + void ignoreReturn(string str) + { + import dmd.id : Id; + + if (str != "return") + { + // don't write 'ref' for ctors + if ((ident == Id.ctor) && str == "ref") + return; + write(str); + write(' '); + } + } + + t.attributesApply(&ignoreReturn); + + if (t.linkage > LINK.d) + { + writeLinkage(t.linkage); + write(' '); + } + if (ident && ident.toHChars2() != ident.toChars()) + { + // Don't print return type for ctor, dtor, unittest, etc + } + else if (t.next) + { + writeTypeWithIdent(t.next, null); + if (ident) + write(' '); + } + if (ident) + write(ident.toString()); + if (td) + { + write('('); + foreach (i, p; *td.origParameters) + { + if (i) + write(", "); + p.accept(this); + } + write(')'); + } + writeParamList(t.parameterList); + if (t.isreturn) + { + write(" return"); + } + t.inuse--; + } + + extern (D) void writeFuncIdentWithPostfix(TypeFunction t, const char[] ident, bool isStatic) + { + if (t.inuse) + { + t.inuse = 2; // flag error to caller + return; + } + t.inuse++; + if (t.linkage > LINK.d) + { + writeLinkage(t.linkage); + write(' '); + } + if (t.linkage == LINK.objc && isStatic) + write("static "); + if (t.next) + { + writeTypeWithIdent(t.next, null); + if (ident) + write(' '); + } + if (ident) + write(ident); + writeParamList(t.parameterList); + /* Use postfix style for attributes */ + if (t.mod) + { + write(' '); + write(MODtoString(t.mod)); + } + + void dg(string str) + { + write(' '); + write(str); + } + + t.attributesApply(&dg); + + t.inuse--; + } + + void writeType(Type t) + { + void visitType(Type _) + { + assert(0); + } + + void visitError(TypeError _) + { + write("_error_"); + } + + void visitBasic(TypeBasic t) + { + write(t.toString()); + } + + void visitTraits(TypeTraits t) + { + writeExpr(t.exp); + } + + void visitVector(TypeVector t) + { + write("__vector("); + writeWithMask(t.basetype, t.mod); + write(")"); + } + + void visitSArray(TypeSArray t) + { + writeWithMask(t.next, t.mod); + write('['); + writeSize(t.dim); + write(']'); + } + + void visitDArray(TypeDArray t) + { + Type ut = t.castMod(0); + if (declString) + goto L1; + if (ut.equals(Type.tstring)) + write("string"); + else if (ut.equals(Type.twstring)) + write("wstring"); + else if (ut.equals(Type.tdstring)) + write("dstring"); + else + { + L1: + writeWithMask(t.next, t.mod); + write("[]"); + } + } + + void visitAArray(TypeAArray t) + { + writeWithMask(t.next, t.mod); + write('['); + writeWithMask(t.index, 0); + write(']'); + } + + void visitPointer(TypePointer t) + { + if (t.next.ty == Tfunction) + writeFuncIdentWithPostfix(cast(TypeFunction) t.next, "function", false); + else + { + writeWithMask(t.next, t.mod); + write('*'); + } + } + + void visitReference(TypeReference t) + { + writeWithMask(t.next, t.mod); + write('&'); + } + + void visitFunction(TypeFunction t) + { + writeFuncIdentWithPostfix(t, null, false); + } + + void visitDelegate(TypeDelegate t) + { + writeFuncIdentWithPostfix(cast(TypeFunction) t.next, "delegate", false); + } + + void visitTypeQualifiedHelper(TypeQualified t) + { + foreach (id; t.idents) + { + switch (id.dyncast()) with (DYNCAST) + { + case dsymbol: + write('.'); + ASTCodegen.TemplateInstance ti = cast(ASTCodegen.TemplateInstance) id; + ti.accept(this); + break; + case expression: + write('['); + writeExpr(cast(ASTCodegen.Expression) id); + write(']'); + break; + case type: + write('['); + writeType(cast(Type) id); + write(']'); + break; + default: + write('.'); + write(id.toString()); + } + } + } + + void visitIdentifier(TypeIdentifier t) + { + write(t.ident.toString()); + visitTypeQualifiedHelper(t); + } + + void visitInstance(TypeInstance t) + { + t.tempinst.accept(this); + visitTypeQualifiedHelper(t); + } + + void visitTypeof(TypeTypeof t) + { + write("typeof("); + writeExpr(t.exp); + write(')'); + visitTypeQualifiedHelper(t); + } + + void visitReturn(TypeReturn t) + { + write("typeof(return)"); + visitTypeQualifiedHelper(t); + } + + void visitEnum(TypeEnum t) + { + write(t.sym.toString()); + } + + 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. + ASTCodegen.TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null; + if (ti && ti.aliasdecl == t.sym) + write(ti.toString()); + else + write(t.sym.toString()); + } + + 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. + ASTCodegen.TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null; + if (ti && ti.aliasdecl == t.sym) + write(ti.toString()); + else + write(t.sym.toString()); + } + + void visitTag(TypeTag t) + { + if (t.mod & MODFlags.const_) + write("const "); + write(Token.toString(t.tok)); + write(' '); + if (t.id) + write(t.id.toString()); + if (t.tok == TOK.enum_ && t.base && t.base.ty != TY.Tint32) + { + write(" : "); + writeWithMask(t.base, t.mod); + } + } + + void visitTuple(TypeTuple t) + { + writeParamList(ParameterList(t.arguments, VarArg.none)); + } + + void visitSlice(TypeSlice t) + { + writeWithMask(t.next, t.mod); + write('['); + writeSize(t.lwr); + write(" .. "); + writeSize(t.upr); + write(']'); + } + + void visitNull(TypeNull _) + { + write("typeof(null)"); + } + + void visitMixin(TypeMixin t) + { + write("mixin("); + writeArgs(t.exps); + write(')'); + } + + void visitNoreturn(TypeNoreturn _) + { + write("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); + } + } + + void writeLinkage(LINK linkage) + { + const s = linkageToString(linkage); + if (s.length) + { + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + write(s); + write(')'); + } + } + + void writeParam(Parameter p) + { + if (p.userAttribDecl) + { + write('@'); + + bool isAnonymous = p.userAttribDecl.atts.length > 0 + && !(*p.userAttribDecl.atts)[0].isCallExp(); + if (isAnonymous) + write('('); + + writeArgs(p.userAttribDecl.atts); + + if (isAnonymous) + write(')'); + write(' '); + } + if (p.storageClass & STC.auto_) + write("auto "); + + StorageClass stc = p.storageClass; + if (p.storageClass & STC.in_) + { + write("in "); + } + else if (p.storageClass & STC.lazy_) + write("lazy "); + else if (p.storageClass & STC.alias_) + write("alias "); + + if (p.type && p.type.mod & MODFlags.shared_) + stc &= ~STC.shared_; + + writeStc(stc & ( + STC.const_ | STC.immutable_ | STC.wild | STC.shared_ | STC.return_ + | STC.returninferred | STC.scope_ | STC.scopeinferred | STC.out_ + | STC.ref_ | STC.returnScope)); + + import core.stdc.string : strncmp; + + if (p.storageClass & STC.alias_) + { + if (p.ident) + write(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 + write(p.ident.toString()); + } + else + { + writeTypeWithIdent(p.type, p.ident, (stc & STC.in_) ? MODFlags.const_ : 0); + } + + if (p.defaultArg) + { + write(" = "); + writeExprWithPrecedence(p.defaultArg, PREC.assign); + } + } + + void writeParamList(ParameterList pl) + { + if (config.dfmt_space_before_function_parameters) + write(' '); + write('('); + foreach (i; 0 .. pl.length) + { + if (i) + write(", "); + writeParam(pl[i]); + } + final switch (pl.varargs) + { + case VarArg.none: + break; + + case VarArg.variadic: + if (pl.length) + write(", "); + + writeStc(pl.stc); + goto case VarArg.typesafe; + + case VarArg.typesafe: + write("..."); + break; + + case VarArg.KRvariadic: + break; + } + write(')'); + } + + void writeVisibility(ASTCodegen.Visibility vis) + { + write(visibilityToString(vis.kind)); + if (vis.kind == ASTCodegen.Visibility.Kind.package_ && vis.pkg) + { + write('('); + write(vis.pkg.toPrettyChars(true).toDString()); + write(')'); + } + } + + extern (D) string visibilityToString(ASTCodegen.Visibility.Kind kind) nothrow pure @safe + { + with (ASTCodegen.Visibility.Kind) + { + immutable string[7] a = [ + none: "none", private_: "private", package_: "package", + protected_: "protected", public_: "public", export_: "export" + ]; + return a[kind]; + } + } + + void writeTiArgs(ASTCodegen.TemplateInstance ti) + { + write('!'); + if (ti.nest) + { + write("(...)"); + return; + } + if (!ti.tiargs) + { + write("()"); + return; + } + if (ti.tiargs.length == 1) + { + import dmd.dtemplate; + + 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.length == 0)) + { + write(t.toString()); + return; + } + } + else if (ASTCodegen.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_) + { + write(e.toString()); + return; + } + } + } + write('('); + ti.nestUp(); + foreach (i, arg; *ti.tiargs) + { + if (i) + write(", "); + writeObject(arg); + } + ti.nestDown(); + write(')'); + } + /******************************************* + * Visitors for AST nodes + */ + void visitDsymbol(ASTCodegen.Dsymbol s) + { + write(s.toString()); + } + + void visitStaticAssert(ASTCodegen.StaticAssert s) + { + write(s.kind().toDString()); + write('('); + writeExpr(s.exp); + if (s.msgs) + { + foreach (m; (*s.msgs)[]) + { + write(", "); + writeExpr(m); + } + } + write(");"); + newline(); + } + + void visitDebugSymbol(ASTCodegen.DebugSymbol s) + { + write("debug = "); + write(s.ident.toString()); + write(';'); + newline(); + } + + void visitVersionSymbol(ASTCodegen.VersionSymbol s) + { + write("version = "); + write(s.ident.toString()); + write(';'); + newline(); + } + + void visitEnumMember(ASTCodegen.EnumMember em) + { + if (em.type) + writeTypeWithIdent(em.type, em.ident); + else + write(em.ident.toString()); + if (em.value) + { + write(" = "); + writeExpr(em.value); + } + } + + void visitImport(ASTCodegen.Import imp) + { + if (imp.isstatic) + write("static "); + write("import "); + if (imp.aliasId) + { + write(imp.aliasId.toString()); + write(" = "); + } + foreach (const pid; imp.packages) + { + write(pid.toString()); + write("."); + } + write(imp.id.toString()); + if (imp.names.length) + { + if (config.dfmt_selective_import_space) + write(' '); + write(": "); + foreach (const i, const name; imp.names) + { + if (i) + write(", "); + const _alias = imp.aliases[i]; + if (_alias) + { + write(_alias.toString()); + write(" = "); + write(name.toString()); + } + else + write(name.toString()); + } + } + + write(';'); + newline(); + } + + void visitAliasThis(ASTCodegen.AliasThis d) + { + write("alias "); + write(d.ident.toString()); + write(" this;"); + newline(); + } + + override void visitAttribDeclaration(ASTCodegen.AttribDeclaration d) + { + if (isNewline) + { + newline(); + } + if (auto stcd = d.isStorageClassDeclaration) + { + writeStc(stcd.stc); + } + + if (!d.decl) + { + write(';'); + newline(); + return; + } + + if (d.decl.length == 0) + { + write("{}"); + } + else if (d.decl.length == 1) + { + (*d.decl)[0].accept(this); + return; + } + else + { + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (de; *d.decl) + de.accept(this); + depth--; + write('}'); + } + newline(); + } + + void visitStorageClassDeclaration(ASTCodegen.StorageClassDeclaration d) + { + visitAttribDeclaration(d); + } + + void visitDeprecatedDeclaration(ASTCodegen.DeprecatedDeclaration d) + { + write("deprecated("); + writeExpr(d.msg); + write(") "); + visitAttribDeclaration(d); + } + + void visitLinkDeclaration(ASTCodegen.LinkDeclaration d) + { + write("extern"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + write(linkageToString(d.linkage)); + write(") "); + visitAttribDeclaration(d); + } + + void visitCPPMangleDeclaration(ASTCodegen.CPPMangleDeclaration d) + { + string s; + final switch (d.cppmangle) + { + case CPPMANGLE.asClass: + s = "class"; + break; + case CPPMANGLE.asStruct: + s = "struct"; + break; + case CPPMANGLE.def: + break; + } + write("extern (C++, "); + write(s); + write(") "); + visitAttribDeclaration(d); + } + + void visitVisibilityDeclaration(ASTCodegen.VisibilityDeclaration d) + { + if (isNewline) + { + newline(); + } + writeVisibility(d.visibility); + ASTCodegen.AttribDeclaration ad = cast(ASTCodegen.AttribDeclaration) d; + if (ad.decl.length <= 1) + write(' '); + if (ad.decl.length == 1 && (*ad.decl)[0].isVisibilityDeclaration) + visitAttribDeclaration((*ad.decl)[0].isVisibilityDeclaration); + else + visitAttribDeclaration(d); + } + + void visitAlignDeclaration(ASTCodegen.AlignDeclaration d) + { + if (d.exps) + { + foreach (i, exp; (*d.exps)[]) + { + if (i) + write(' '); + write(format("align (%s)", exp.toString())); + } + if (d.decl && d.decl.length < 2) + write(' '); + } + else + write("align "); + + visitAttribDeclaration(d.isAttribDeclaration()); + } + + void visitAnonDeclaration(ASTCodegen.AnonDeclaration d) + { + write(d.isunion ? "union" : "struct"); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + if (d.decl) + { + foreach (de; *d.decl) + de.accept(this); + } + depth--; + write('}'); + newline(); + } + + void visitPragmaDeclaration(ASTCodegen.PragmaDeclaration d) + { + write("pragma"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + write(d.ident.toString()); + if (d.args && d.args.length) + { + write(", "); + writeArgs(d.args); + } + + write(')'); + visitAttribDeclaration(d); + } + + void visitConditionalDeclaration(ASTCodegen.ConditionalDeclaration d) + { + d.condition.accept(this); + if (d.decl || d.elsedecl) + { + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + if (d.decl) + { + foreach (de; *d.decl) + de.accept(this); + } + depth--; + write('}'); + if (d.elsedecl) + { + newline(); + write("else"); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (de; *d.elsedecl) + de.accept(this); + depth--; + write('}'); + } + } + else + write(':'); + newline(); + } + + void visitStaticForeachDeclaration(ASTCodegen.StaticForeachDeclaration s) + { + void foreachWithoutBody(ASTCodegen.ForeachStatement s) + { + write(Token.toString(s.op)); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + foreach (i, p; *s.parameters) + { + if (i) + write(", "); + writeStc(p.storageClass); + if (p.type) + writeTypeWithIdent(p.type, p.ident); + else + write(p.ident.toString()); + } + write("; "); + writeExpr(s.aggr); + write(')'); + newline(); + } + + void foreachRangeWithoutBody(ASTCodegen.ForeachRangeStatement s) + { + /* s.op ( prm ; lwr .. upr ) + */ + write(Token.toString(s.op)); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + if (s.prm.type) + writeTypeWithIdent(s.prm.type, s.prm.ident); + else + write(s.prm.ident.toString()); + write("; "); + writeExpr(s.lwr); + write(" .. "); + writeExpr(s.upr); + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + } + + write("static "); + if (s.sfe.aggrfe) + { + foreachWithoutBody(s.sfe.aggrfe); + } + else + { + assert(s.sfe.rangefe); + foreachRangeWithoutBody(s.sfe.rangefe); + } + write('{'); + newline(); + depth++; + visitAttribDeclaration(s); + depth--; + write('}'); + newline(); + + } + + void visitMixinDeclaration(ASTCodegen.MixinDeclaration d) + { + write("mixin("); + writeArgs(d.exps); + write(");"); + newline(); + } + + void visitUserAttributeDeclaration(ASTCodegen.UserAttributeDeclaration d) + { + write("@("); + writeArgs(d.atts); + write(')'); + visitAttribDeclaration(d); + } + + void visitTemplateConstraint(ASTCodegen.Expression constraint) + { + + if (!constraint) + return; + + final switch (config.dfmt_template_constraint_style) + { + case TemplateConstraintStyle._unspecified: + // Fallthrough to the default case + case TemplateConstraintStyle.conditional_newline_indent: + useTempBuf = true; + depth++; + if (config.dfmt_single_template_constraint_indent) + depth++; + break; + case TemplateConstraintStyle.always_newline_indent: + newline(); + depth++; + if (config.dfmt_single_template_constraint_indent) + depth++; + break; + case TemplateConstraintStyle.conditional_newline: + useTempBuf = true; + break; + case TemplateConstraintStyle.always_newline: + newline(); + break; + } + + write("if"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + writeExpr(constraint); + write(')'); + + final switch (config.dfmt_template_constraint_style) + { + case TemplateConstraintStyle._unspecified: + // Fallthrough to the default case + case TemplateConstraintStyle.conditional_newline_indent: + if (!conditionalNewline()) + buf.put(' '); + writeTempBuf(); + goto case; + case TemplateConstraintStyle.always_newline_indent: + depth--; + if (config.dfmt_single_template_constraint_indent) + depth--; + break; + case TemplateConstraintStyle.conditional_newline: + if (!conditionalNewline()) + buf.put(' '); + writeTempBuf(); + break; + case TemplateConstraintStyle.always_newline: + break; + } + } + + override void visitBaseClasses(ASTCodegen.ClassDeclaration d) + { + if (!d || !d.baseclasses.length) + return; + if (!d.isAnonymous()) + write(" : "); + foreach (i, b; *d.baseclasses) + { + if (i) + write(", "); + writeTypeWithIdent(b.type, null); + } + } + + override bool visitEponymousMember(ASTCodegen.TemplateDeclaration d) + { + if (!d.members || d.members.length != 1) + return false; + ASTCodegen.Dsymbol onemember = (*d.members)[0]; + if (onemember.ident != d.ident) + return false; + if (ASTCodegen.FuncDeclaration fd = onemember.isFuncDeclaration()) + { + assert(fd.type); + writeStc(fd.storage_class); + writeFuncIdentWithPrefix(cast(TypeFunction) fd.type, d.ident, d); + visitTemplateConstraint(d.constraint); + writeFuncBody(fd); + return true; + } + if (ASTCodegen.AggregateDeclaration ad = onemember.isAggregateDeclaration()) + { + write(ad.kind().toDString()); + write(' '); + write(ad.ident.toString()); + write('('); + visitTemplateParameters(d.parameters); + write(')'); + visitTemplateConstraint(d.constraint); + visitBaseClasses(ad.isClassDeclaration()); + if (ad.members) + { + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (s; *ad.members) + s.accept(this); + depth--; + write('}'); + } + else + write(';'); + newline(); + return true; + } + if (ASTCodegen.VarDeclaration vd = onemember.isVarDeclaration()) + { + if (d.constraint) + return false; + writeStc(vd.storage_class); + if (vd.type) + writeTypeWithIdent(vd.type, vd.ident); + else + write(vd.ident.toString()); + write('('); + visitTemplateParameters(d.parameters); + write(')'); + if (vd._init) + { + write(" = "); + ExpInitializer ie = vd._init.isExpInitializer(); + if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit)) + writeExpr((cast(ASTCodegen.AssignExp) ie.exp).e2); + else + writeInitializer(vd._init); + } + write(';'); + newline(); + return true; + } + return false; + } + + void visitTemplateDeclaration(ASTCodegen.TemplateDeclaration d) + { + write("template"); + write(' '); + write(d.ident.toString()); + write('('); + visitTemplateParameters(d.parameters); + write(')'); + visitTemplateConstraint(d.constraint); + } + + void visitTemplateInstance(ASTCodegen.TemplateInstance ti) + { + write(ti.name.toString()); + writeTiArgs(ti); + } + + void visitTemplateMixin(ASTCodegen.TemplateMixin tm) + { + write("mixin "); + writeTypeWithIdent(tm.tqual, null); + writeTiArgs(tm); + if (tm.ident && tm.ident.toString() != "__mixin") + { + write(' '); + write(tm.ident.toString()); + } + write(';'); + newline(); + } + + void visitEnumDeclaration(ASTCodegen.EnumDeclaration d) + { + write("enum "); + if (d.ident) + { + write(d.ident.toString()); + } + if (d.memtype) + { + write(" : "); + writeTypeWithIdent(d.memtype, null); + } + if (!d.members) + { + write(';'); + newline(); + return; + } + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (em; *d.members) + { + if (!em) + continue; + em.accept(this); + write(','); + newline(); + } + depth--; + write('}'); + newline(); + } + + void visitNspace(ASTCodegen.Nspace d) + { + write("extern (C++, "); + write(d.ident.toString()); + write(')'); + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (s; *d.members) + s.accept(this); + depth--; + write('}'); + newline(); + } + + void visitStructDeclaration(ASTCodegen.StructDeclaration d) + { + write(d.kind().toDString()); + write(' '); + if (!d.isAnonymous()) + write(d.toString()); + if (!d.members) + { + write(';'); + newline(); + return; + } + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (s; *d.members) + s.accept(this); + depth--; + write('}'); + newline(); + } + + void visitClassDeclaration(ASTCodegen.ClassDeclaration d) + { + if (!d.isAnonymous()) + { + write(d.kind().toDString()); + write(' '); + write(d.ident.toString()); + } + visitBaseClasses(d); + if (d.members) + { + if (config.dfmt_brace_style == BraceStyle.allman) + newline(); + else + write(' '); + write('{'); + newline(); + depth++; + foreach (s; *d.members) + s.accept(this); + depth--; + write('}'); + } + else + write(';'); + newline(); + } + + void visitAliasDeclaration(ASTCodegen.AliasDeclaration d) + { + if (d.storage_class & STC.local) + return; + write("alias "); + if (d.aliassym) + { + write(d.ident.toString()); + write(" = "); + writeStc(d.storage_class); + /* + 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()) + { + write(d.aliassym.ident.toString()); + } + else + { + d.aliassym.accept(this); + } + } + else if (d.type.ty == Tfunction) + { + writeStc(d.storage_class); + writeTypeWithIdent(d.type, d.ident); + } + else if (d.ident) + { + import dmd.id : Id; + + declString = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring); + write(d.ident.toString()); + write(" = "); + writeStc(d.storage_class); + writeTypeWithIdent(d.type, null); + declString = false; + } + write(';'); + newline(); + } + + void visitAliasAssign(ASTCodegen.AliasAssign d) + { + write(d.ident.toString()); + write(" = "); + if (d.aliassym) + d.aliassym.accept(this); + else // d.type + writeTypeWithIdent(d.type, null); + write(';'); + newline(); + } + + void visitVarDeclaration(ASTCodegen.VarDeclaration d) + { + if (d.storage_class & STC.local) + return; + writeVarDecl(d, false); + write(';'); + newline(); + } + + void visitFuncDeclaration(ASTCodegen.FuncDeclaration f) + { + if (isNewline) + { + newline(); + } + writeStc(f.storage_class); + auto tf = cast(TypeFunction) f.type; + writeTypeWithIdent(tf, f.ident); + writeFuncBody(f); + } + + void visitFuncLiteralDeclaration(ASTCodegen.FuncLiteralDeclaration f) + { + if (f.type.ty == Terror) + { + write("__error"); + return; + } + if (f.tok != TOK.reserved) + { + write(f.kind().toDString()); + write(' '); + } + TypeFunction tf = cast(TypeFunction) f.type; + + if (!f.inferRetType && tf.next) + writeTypeWithIdent(tf.next, null); + writeParamList(tf.parameterList); + + // https://issues.dlang.org/show_bug.cgi?id=20074 + void printAttribute(string str) + { + write(' '); + write(str); + } + + tf.attributesApply(&printAttribute); + + ASTCodegen.CompoundStatement cs = f.fbody.isCompoundStatement(); + ASTCodegen.Statement s1; + s1 = !cs ? f.fbody : null; + ASTCodegen.ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null; + if (rs && rs.exp) + { + write(" => "); + writeExpr(rs.exp); + } + else + { + writeFuncBody(f); + } + } + + void visitPostBlitDeclaration(ASTCodegen.PostBlitDeclaration d) + { + writeStc(d.storage_class); + write("this(this)"); + writeFuncBody(d); + } + + void visitDtorDeclaration(ASTCodegen.DtorDeclaration d) + { + writeStc(d.storage_class); + write("~this()"); + writeFuncBody(d); + } + + void visitStaticCtorDeclaration(ASTCodegen.StaticCtorDeclaration d) + { + writeStc(d.storage_class & ~STC.static_); + if (d.isSharedStaticCtorDeclaration()) + write("shared "); + write("static this()"); + writeFuncBody(d); + } + + void visitStaticDtorDeclaration(ASTCodegen.StaticDtorDeclaration d) + { + writeStc(d.storage_class & ~STC.static_); + if (d.isSharedStaticDtorDeclaration()) + write("shared "); + write("static ~this()"); + writeFuncBody(d); + } + + void visitInvariantDeclaration(ASTCodegen.InvariantDeclaration d) + { + writeStc(d.storage_class); + write("invariant"); + if (auto es = d.fbody.isExpStatement()) + { + assert(es.exp && es.exp.op == EXP.assert_); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + writeExpr((cast(ASTCodegen.AssertExp) es.exp).e1); + write(");"); + newline(); + } + else + { + writeFuncBody(d); + } + } + + void visitUnitTestDeclaration(ASTCodegen.UnitTestDeclaration d) + { + writeStc(d.storage_class); + write("unittest"); + writeFuncBody(d); + } + + void visitBitFieldDeclaration(ASTCodegen.BitFieldDeclaration d) + { + writeStc(d.storage_class); + Identifier id = d.isAnonymous() ? null : d.ident; + writeTypeWithIdent(d.type, id); + write(" : "); + writeExpr(d.width); + write(';'); + newline(); + } + + void visitNewDeclaration(ASTCodegen.NewDeclaration d) + { + writeStc(d.storage_class & ~STC.static_); + write("new();"); + } + + void visitModule(ASTCodegen.Module m) + { + if (m.md) + { + if (m.userAttribDecl) + { + write("@("); + writeArgs(m.userAttribDecl.atts); + write(')'); + newline(); + } + if (m.md.isdeprecated) + { + if (m.md.msg) + { + write("deprecated("); + writeExpr(m.md.msg); + write(") "); + } + else + write("deprecated "); + } + write("module "); + write(m.md.toString()); + write(';'); + newline(); + newline(); + } + + foreach (s; *m.members) + { + s.accept(this); + } + } + + void visitDebugCondition(ASTCodegen.DebugCondition c) + { + write("debug"); + if (c.ident) + { + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + write(c.ident.toString()); + write(')'); + } + } + + void visitVersionCondition(ASTCodegen.VersionCondition c) + { + write("version"); + if (c.ident) + { + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + write(c.ident.toString()); + write(')'); + } + } + + void visitStaticIfCondition(ASTCodegen.StaticIfCondition c) + { + write("static if"); + if (config.dfmt_space_after_keywords) + write(' '); + write('('); + writeExpr(c.exp); + write(')'); + } + + void visitTemplateTypeParameter(ASTCodegen.TemplateTypeParameter tp) + { + write(tp.ident.toString()); + if (tp.specType) + { + write(" : "); + writeTypeWithIdent(tp.specType, null); + } + if (tp.defaultType) + { + write(" = "); + writeTypeWithIdent(tp.defaultType, null); + } + } + + void visitTemplateThisParameter(ASTCodegen.TemplateThisParameter tp) + { + write("this "); + visit(cast(ASTCodegen.TemplateTypeParameter) tp); + } + + void visitTemplateAliasParameter(ASTCodegen.TemplateAliasParameter tp) + { + write("alias "); + if (tp.specType) + writeTypeWithIdent(tp.specType, tp.ident); + else + write(tp.ident.toString()); + if (tp.specAlias) + { + write(" : "); + writeObject(tp.specAlias); + } + if (tp.defaultAlias) + { + write(" = "); + writeObject(tp.defaultAlias); + } + } + + void visitTemplateValueParameter(ASTCodegen.TemplateValueParameter tp) + { + writeTypeWithIdent(tp.valType, tp.ident); + if (tp.specValue) + { + write(" : "); + writeExpr(tp.specValue); + } + if (tp.defaultValue) + { + write(" = "); + writeExpr(tp.defaultValue); + } + } + + void visitTemplateTupleParameter(ASTCodegen.TemplateTupleParameter tp) + { + write(tp.ident.toString()); + write("..."); + } + +override: + // dfmt off + void visit(ASTCodegen.Dsymbol s) { visitDsymbol(s); } + void visit(ASTCodegen.StaticAssert s) { visitStaticAssert(s); } + void visit(ASTCodegen.DebugSymbol s) { visitDebugSymbol(s); } + void visit(ASTCodegen.VersionSymbol s) { visitVersionSymbol(s); } + void visit(ASTCodegen.EnumMember em) { visitEnumMember(em); } + void visit(ASTCodegen.Import imp) { visitImport(imp); } + void visit(ASTCodegen.AliasThis d) { visitAliasThis(d); } + void visit(ASTCodegen.AttribDeclaration d) { visitAttribDeclaration(d); } + void visit(ASTCodegen.StorageClassDeclaration d) { visitStorageClassDeclaration(d); } + void visit(ASTCodegen.DeprecatedDeclaration d) { visitDeprecatedDeclaration(d); } + void visit(ASTCodegen.LinkDeclaration d) { visitLinkDeclaration(d); } + void visit(ASTCodegen.CPPMangleDeclaration d) { visitCPPMangleDeclaration(d); } + void visit(ASTCodegen.VisibilityDeclaration d) { visitVisibilityDeclaration(d); } + void visit(ASTCodegen.AlignDeclaration d) { visitAlignDeclaration(d); } + void visit(ASTCodegen.AnonDeclaration d) { visitAnonDeclaration(d); } + void visit(ASTCodegen.PragmaDeclaration d) { visitPragmaDeclaration(d); } + void visit(ASTCodegen.ConditionalDeclaration d) { visitConditionalDeclaration(d); } + void visit(ASTCodegen.StaticForeachDeclaration s) { visitStaticForeachDeclaration(s); } + void visit(ASTCodegen.MixinDeclaration d) { visitMixinDeclaration(d); } + void visit(ASTCodegen.UserAttributeDeclaration d) { visitUserAttributeDeclaration(d); } + void visit(ASTCodegen.TemplateDeclaration d) { visitTemplateDeclaration(d); } + void visit(ASTCodegen.TemplateInstance ti) { visitTemplateInstance(ti); } + void visit(ASTCodegen.TemplateMixin tm) { visitTemplateMixin(tm); } + void visit(ASTCodegen.EnumDeclaration d) { visitEnumDeclaration(d); } + void visit(ASTCodegen.Nspace d) { visitNspace(d); } + void visit(ASTCodegen.StructDeclaration d) { visitStructDeclaration(d); } + void visit(ASTCodegen.ClassDeclaration d) { visitClassDeclaration(d); } + void visit(ASTCodegen.AliasDeclaration d) { visitAliasDeclaration(d); } + void visit(ASTCodegen.AliasAssign d) { visitAliasAssign(d); } + void visit(ASTCodegen.VarDeclaration d) { visitVarDeclaration(d); } + void visit(ASTCodegen.FuncDeclaration f) { visitFuncDeclaration(f); } + void visit(ASTCodegen.FuncLiteralDeclaration f) { visitFuncLiteralDeclaration(f); } + void visit(ASTCodegen.PostBlitDeclaration d) { visitPostBlitDeclaration(d); } + void visit(ASTCodegen.DtorDeclaration d) { visitDtorDeclaration(d); } + void visit(ASTCodegen.StaticCtorDeclaration d) { visitStaticCtorDeclaration(d); } + void visit(ASTCodegen.StaticDtorDeclaration d) { visitStaticDtorDeclaration(d); } + void visit(ASTCodegen.InvariantDeclaration d) { visitInvariantDeclaration(d); } + void visit(ASTCodegen.UnitTestDeclaration d) { visitUnitTestDeclaration(d); } + void visit(ASTCodegen.BitFieldDeclaration d) { visitBitFieldDeclaration(d); } + void visit(ASTCodegen.NewDeclaration d) { visitNewDeclaration(d); } + void visit(ASTCodegen.Module m) { visitModule(m); } + void visit(ASTCodegen.DebugCondition m) { visitDebugCondition(m); } + void visit(ASTCodegen.VersionCondition m) { visitVersionCondition(m); } + void visit(ASTCodegen.StaticIfCondition m) { visitStaticIfCondition(m); } + void visit(ASTCodegen.TemplateTypeParameter m) { visitTemplateTypeParameter(m); } + void visit(ASTCodegen.TemplateThisParameter m) { visitTemplateThisParameter(m); } + void visit(ASTCodegen.TemplateAliasParameter m) { visitTemplateAliasParameter(m); } + void visit(ASTCodegen.TemplateValueParameter m) { visitTemplateValueParameter(m); } + void visit(ASTCodegen.TemplateTupleParameter m) { visitTemplateTupleParameter(m); } + // dfmt on +} diff --git a/src/dfmt/ast_info.d b/src/dfmt/ast_info.d deleted file mode 100644 index 34a8ede..0000000 --- a/src/dfmt/ast_info.d +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright Brian Schott 2015. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dfmt.ast_info; - -import dparse.lexer; -import dparse.ast; - -enum BraceIndentInfoFlags -{ - tempIndent = 1 << 0, -} - -struct BraceIndentInfo -{ - size_t startLocation; - size_t endLocation; - - uint flags; - - uint beginIndentLevel; -} - -struct StructInitializerInfo -{ - size_t startLocation; - size_t endLocation; -} - -/// AST information that is needed by the formatter. -struct ASTInformation -{ - /// Sorts the arrays so that binary search will work on them - void cleanup() - { - import std.algorithm : sort, uniq; - import std.array : array; - - sort(doubleNewlineLocations); - sort(spaceAfterLocations); - sort(unaryLocations); - sort(attributeDeclarationLines); - sort(atAttributeStartLocations); - sort(caseEndLocations); - sort(structInitStartLocations); - sort(structInitEndLocations); - sort(funLitStartLocations); - sort(funLitEndLocations); - sort(conditionalWithElseLocations); - sort(conditionalStatementLocations); - sort(arrayStartLocations); - sort(assocArrayStartLocations); - sort(contractLocations); - sort(constraintLocations); - sort(constructorDestructorLocations); - sort(staticConstructorDestructorLocations); - sort(sharedStaticConstructorDestructorLocations); - sort!((a,b) => a.endLocation < b.endLocation) - (indentInfoSortedByEndLocation); - sort!((a,b) => a.endLocation < b.endLocation) - (structInfoSortedByEndLocation); - sort(ufcsHintLocations); - ufcsHintLocations = ufcsHintLocations.uniq().array(); - sort(ternaryColonLocations); - sort(namedArgumentColonLocations); - } - - /// Locations of end braces for struct bodies - size_t[] doubleNewlineLocations; - - /// Locations of tokens where a space is needed (such as the '*' in a type) - size_t[] spaceAfterLocations; - - /// Locations of unary operators - size_t[] unaryLocations; - - /// Lines containing attribute declarations - size_t[] attributeDeclarationLines; - - /// Lines containing attribute declarations that can be followed by a new line - size_t[] atAttributeStartLocations; - - /// Case statement colon locations - size_t[] caseEndLocations; - - /// Opening braces of struct initializers - size_t[] structInitStartLocations; - - /// Closing braces of struct initializers - size_t[] structInitEndLocations; - - /// Opening braces of function literals - size_t[] funLitStartLocations; - - /// Closing braces of function literals - size_t[] funLitEndLocations; - - /// Locations of aggregate bodies (struct, class, union) - size_t[] aggregateBodyLocations; - - /// Locations of function bodies - size_t[] funBodyLocations; - - /// Conditional statements that have matching "else" statements - size_t[] conditionalWithElseLocations; - - /// Conditional statement locations - size_t[] conditionalStatementLocations; - - /// Locations of start locations of array initializers - size_t[] arrayStartLocations; - - /// Locations of start locations of associative array initializers - size_t[] assocArrayStartLocations; - - /// Locations of "in" and "out" tokens that begin contracts - size_t[] contractLocations; - - /// Locations of template constraint "if" tokens - size_t[] constraintLocations; - - /// Locations of constructor/destructor "shared" tokens ? - size_t[] sharedStaticConstructorDestructorLocations; - - /// Locations of constructor/destructor "static" tokens ? - size_t[] staticConstructorDestructorLocations; - - /// Locations of constructor/destructor "this" tokens ? - size_t[] constructorDestructorLocations; - - /// Locations of '.' characters that might be UFCS chains. - size_t[] ufcsHintLocations; - - BraceIndentInfo[] indentInfoSortedByEndLocation; - - /// Opening & closing braces of struct initializers - StructInitializerInfo[] structInfoSortedByEndLocation; - - /// Locations ternary expression colons. - size_t[] ternaryColonLocations; - - /// Locations of named arguments of function call or struct constructor. - size_t[] namedArgumentColonLocations; -} - -/// Collects information from the AST that is useful for the formatter -final class FormatVisitor : ASTVisitor -{ - alias visit = ASTVisitor.visit; - - /** - * Params: - * astInformation = the AST information that will be filled in - */ - this(ASTInformation* astInformation) - { - this.astInformation = astInformation; - } - - override void visit(const ArrayInitializer arrayInitializer) - { - astInformation.arrayStartLocations ~= arrayInitializer.startLocation; - arrayInitializer.accept(this); - } - - override void visit(const ArrayLiteral arrayLiteral) - { - astInformation.arrayStartLocations ~= arrayLiteral.tokens[0].index; - arrayLiteral.accept(this); - } - - override void visit(const AssocArrayLiteral assocArrayLiteral) - { - astInformation.arrayStartLocations ~= assocArrayLiteral.tokens[0].index; - astInformation.assocArrayStartLocations ~= assocArrayLiteral.tokens[0].index; - assocArrayLiteral.accept(this); - } - - override void visit (const SharedStaticConstructor sharedStaticConstructor) - { - astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticConstructor.location; - sharedStaticConstructor.accept(this); - } - - override void visit (const SharedStaticDestructor sharedStaticDestructor) - { - astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticDestructor.location; - sharedStaticDestructor.accept(this); - } - - override void visit (const StaticConstructor staticConstructor) - { - astInformation.staticConstructorDestructorLocations ~= staticConstructor.location; - staticConstructor.accept(this); - } - - override void visit (const StaticDestructor staticDestructor) - { - astInformation.staticConstructorDestructorLocations ~= staticDestructor.location; - staticDestructor.accept(this); - } - - override void visit (const Constructor constructor) - { - astInformation.constructorDestructorLocations ~= constructor.location; - constructor.accept(this); - } - - override void visit (const Destructor destructor) - { - astInformation.constructorDestructorLocations ~= destructor.index; - destructor.accept(this); - } - - override void visit (const FunctionBody functionBody) - { - if (auto bd = functionBody.specifiedFunctionBody) - { - if (bd.blockStatement) - { - astInformation.funBodyLocations ~= bd.blockStatement.startLocation; - } - } - functionBody.accept(this); - } - - override void visit(const ConditionalDeclaration dec) - { - if (dec.hasElse) - { - auto condition = dec.compileCondition; - if (condition.versionCondition !is null) - { - astInformation.conditionalWithElseLocations - ~= condition.versionCondition.versionIndex; - } - else if (condition.debugCondition !is null) - { - astInformation.conditionalWithElseLocations ~= condition.debugCondition.debugIndex; - } - // Skip "static if" because the formatting for normal "if" handles - // it properly - } - dec.accept(this); - } - - override void visit(const Constraint constraint) - { - astInformation.constraintLocations ~= constraint.location; - constraint.accept(this); - } - - override void visit(const ConditionalStatement statement) - { - auto condition = statement.compileCondition; - if (condition.versionCondition !is null) - { - astInformation.conditionalStatementLocations ~= condition.versionCondition.versionIndex; - } - else if (condition.debugCondition !is null) - { - astInformation.conditionalStatementLocations ~= condition.debugCondition.debugIndex; - } - statement.accept(this); - } - - override void visit(const FunctionLiteralExpression funcLit) - { - if (funcLit.specifiedFunctionBody !is null) - { - const bs = funcLit.specifiedFunctionBody.blockStatement; - - astInformation.funLitStartLocations ~= bs.startLocation; - astInformation.funLitEndLocations ~= bs.endLocation; - astInformation.indentInfoSortedByEndLocation ~= - BraceIndentInfo(bs.startLocation, bs.endLocation); - } - funcLit.accept(this); - } - - override void visit(const DefaultStatement defaultStatement) - { - astInformation.caseEndLocations ~= defaultStatement.colonLocation; - defaultStatement.accept(this); - } - - override void visit(const CaseStatement caseStatement) - { - astInformation.caseEndLocations ~= caseStatement.colonLocation; - caseStatement.accept(this); - } - - override void visit(const CaseRangeStatement caseRangeStatement) - { - astInformation.caseEndLocations ~= caseRangeStatement.colonLocation; - caseRangeStatement.accept(this); - } - - override void visit(const SpecifiedFunctionBody specifiedFunctionBody) - { - if (specifiedFunctionBody.blockStatement !is null) - astInformation.doubleNewlineLocations ~= specifiedFunctionBody.blockStatement.endLocation; - specifiedFunctionBody.accept(this); - } - - override void visit(const StructInitializer structInitializer) - { - astInformation.structInitStartLocations ~= structInitializer.startLocation; - astInformation.structInitEndLocations ~= structInitializer.endLocation; - astInformation.structInfoSortedByEndLocation ~= - StructInitializerInfo(structInitializer.startLocation, structInitializer.endLocation); - astInformation.indentInfoSortedByEndLocation ~= - BraceIndentInfo(structInitializer.startLocation, structInitializer.endLocation); - - structInitializer.accept(this); - } - - override void visit(const EnumBody enumBody) - { - astInformation.doubleNewlineLocations ~= enumBody.endLocation; - enumBody.accept(this); - } - - override void visit(const Unittest unittest_) - { - astInformation.doubleNewlineLocations ~= unittest_.blockStatement.endLocation; - unittest_.accept(this); - } - - override void visit(const Invariant invariant_) - { - if (invariant_.blockStatement !is null) - astInformation.doubleNewlineLocations ~= invariant_.blockStatement.endLocation; - - invariant_.accept(this); - } - - override void visit(const StructBody structBody) - { - astInformation.aggregateBodyLocations ~= structBody.startLocation; - astInformation.doubleNewlineLocations ~= structBody.endLocation; - structBody.accept(this); - } - - override void visit(const TemplateDeclaration templateDeclaration) - { - astInformation.doubleNewlineLocations ~= templateDeclaration.endLocation; - templateDeclaration.accept(this); - } - - override void visit(const TypeSuffix typeSuffix) - { - if (typeSuffix.star.type != tok!"") - astInformation.spaceAfterLocations ~= typeSuffix.star.index; - typeSuffix.accept(this); - } - - override void visit(const UnaryExpression unary) - { - import std.typecons : rebindable; - - int chainLength; - auto u = rebindable(unary); - while (u !is null) - { - if (u.identifierOrTemplateInstance !is null - && u.identifierOrTemplateInstance.templateInstance !is null) - chainLength++; - u = u.unaryExpression; - } - if (chainLength > 1) - { - u = unary; - while (u.unaryExpression !is null) - { - astInformation.ufcsHintLocations ~= u.dotLocation; - u = u.unaryExpression; - } - } - if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&" - || unary.prefix.type == tok!"*" - || unary.prefix.type == tok!"+" || unary.prefix.type == tok!"-") - { - astInformation.unaryLocations ~= unary.prefix.index; - } - unary.accept(this); - } - - override void visit(const AttributeDeclaration attributeDeclaration) - { - astInformation.attributeDeclarationLines ~= attributeDeclaration.line; - attributeDeclaration.accept(this); - } - - override void visit(const FunctionAttribute functionAttribute) - { - if (functionAttribute.atAttribute !is null) - astInformation.atAttributeStartLocations ~= functionAttribute.atAttribute.startLocation; - functionAttribute.accept(this); - } - - override void visit(const MemberFunctionAttribute memberFunctionAttribute) - { - if (memberFunctionAttribute.atAttribute !is null) - astInformation.atAttributeStartLocations ~= memberFunctionAttribute.atAttribute.startLocation; - memberFunctionAttribute.accept(this); - } - - override void visit(const Attribute attribute) - { - if (attribute.atAttribute !is null) - astInformation.atAttributeStartLocations ~= attribute.atAttribute.startLocation; - attribute.accept(this); - } - - override void visit(const StorageClass storageClass) - { - if (storageClass.atAttribute !is null) - astInformation.atAttributeStartLocations ~= storageClass.atAttribute.startLocation; - storageClass.accept(this); - } - - override void visit(const InContractExpression inContractExpression) - { - astInformation.contractLocations ~= inContractExpression.inTokenLocation; - inContractExpression.accept(this); - } - - override void visit(const InStatement inStatement) - { - astInformation.contractLocations ~= inStatement.inTokenLocation; - inStatement.accept(this); - } - - override void visit(const OutContractExpression outContractExpression) - { - astInformation.contractLocations ~= outContractExpression.outTokenLocation; - outContractExpression.accept(this); - } - - override void visit(const OutStatement outStatement) - { - astInformation.contractLocations ~= outStatement.outTokenLocation; - outStatement.accept(this); - } - - override void visit(const TernaryExpression ternaryExpression) - { - astInformation.ternaryColonLocations ~= ternaryExpression.colon.index; - ternaryExpression.accept(this); - } - - override void visit(const FunctionCallExpression functionCall) - { - // Check if function has any arguments. - if (functionCall.arguments.namedArgumentList is null) - { - functionCall.accept(this); - return; - } - - foreach (item; functionCall.arguments.namedArgumentList.items) - { - // Do nothing if not a named argument. - if (item.name == tok!"") - { - continue; - } - - // Find first colon if named argument. - foreach (t; item.tokens) - { - if (t.type == tok!":") - { - astInformation.namedArgumentColonLocations ~= t.index; - break; - } - } - } - - functionCall.accept(this); - } - -private: - ASTInformation* astInformation; -} diff --git a/src/dfmt/config.d b/src/dfmt/config.d index 6b41d4a..f8ba9cf 100644 --- a/src/dfmt/config.d +++ b/src/dfmt/config.d @@ -66,8 +66,6 @@ struct Config /// OptionalBoolean dfmt_reflow_property_chains; /// - OptionalBoolean dfmt_space_after_statement_keyword; - /// OptionalBoolean dfmt_space_before_named_arg_colon; mixin StandardEditorConfigFields; diff --git a/src/dfmt/editorconfig.d b/src/dfmt/editorconfig.d index ce79f7c..b23ab7f 100644 --- a/src/dfmt/editorconfig.d +++ b/src/dfmt/editorconfig.d @@ -123,8 +123,7 @@ EC getConfigFor(EC)(string path) { import std.stdio : File; import std.regex : regex, match; - import std.path : globMatch, dirName, baseName, pathSplitter, buildPath, - absolutePath; + import std.path : globMatch, dirName, baseName, pathSplitter, buildPath, absolutePath; import std.algorithm : reverse, map, filter; import std.array : array; import std.file : isDir; @@ -141,7 +140,9 @@ EC getConfigFor(EC)(string path) EC[] sections = parseConfig!EC(buildPath(pathParts[0 .. i])); if (sections.length) configs ~= sections; - if (!sections.map!(a => a.root).filter!(a => a == OptionalBoolean.t).empty) + if (!sections.map!(a => a.root) + .filter!(a => a == OptionalBoolean.t) + .empty) break; } reverse(configs); @@ -208,7 +209,7 @@ private EC[] parseConfig(EC)(string dir) mixin(configDot) = propertyValue == "true" ? OptionalBoolean.t : OptionalBoolean.f; else - mixin(configDot) = to!(FieldType)(propertyValue); + mixin(configDot) = to!(FieldType)(propertyValue); } } } diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index 0f7e821..1fc9ff1 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -1,4 +1,3 @@ - // Copyright Brian Schott 2015. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -6,16 +5,21 @@ module dfmt.formatter; -import dparse.lexer; -import dparse.parser; -import dparse.rollback_allocator; -import dfmt.ast_info; +import dmd.tokens; +import dmd.parse; +import dmd.id; +import dmd.errorsink; +import dmd.identifier; +import dmd.astcodegen; +import dmd.transitivevisitor; +import dmd.permissivevisitor; +import dmd.frontend; +import dmd.globals; +import dfmt.ast; import dfmt.config; -import dfmt.indentation; -import dfmt.tokens; -import dfmt.wrapping; import std.array; import std.algorithm.comparison : among, max; +import std.stdio : File; /** * Formats the code contained in `buffer` into `output`. @@ -24,2371 +28,16 @@ import std.algorithm.comparison : among, max; * buffer = The raw source code. * output = The output range that will have the formatted code written to it. * formatterConfig = Formatter configuration. - * Returns: `true` if the formatting succeeded, `false` of a lexing error. This - * function can return `true` if parsing failed. + * Returns: `true` if the formatting succeeded, `false` if any error */ -bool format(OutputRange)(string source_desc, ubyte[] buffer, OutputRange output, +bool format(string source_desc, ubyte[] buffer, File.LockingTextWriter output, Config* formatterConfig) { - LexerConfig config; - config.stringBehavior = StringBehavior.source; - config.whitespaceBehavior = WhitespaceBehavior.skip; - LexerConfig parseConfig; - parseConfig.stringBehavior = StringBehavior.source; - parseConfig.whitespaceBehavior = WhitespaceBehavior.skip; - StringCache cache = StringCache(StringCache.defaultBucketCount); - ASTInformation astInformation; - RollbackAllocator allocator; - auto parseTokens = getTokensForParser(buffer, parseConfig, &cache); - auto mod = parseModule(parseTokens, source_desc, &allocator); - auto visitor = new FormatVisitor(&astInformation); - visitor.visit(mod); - astInformation.cleanup(); - auto tokenRange = byToken(buffer, config, &cache); - auto app = appender!(Token[])(); - for (; !tokenRange.empty(); tokenRange.popFront()) - app.put(tokenRange.front()); - auto tokens = app.data; - if (!tokenRange.messages.empty) - return false; - auto depths = generateDepthInfo(tokens); - auto tokenFormatter = TokenFormatter!OutputRange(buffer, tokens, depths, - output, &astInformation, formatterConfig); - tokenFormatter.format(); + initDMD(); + global.params.useUnitTests = true; + auto module_ = parseModule(source_desc)[0]; + scope v = new FormatVisitor(output, formatterConfig); + v.visit(module_); + return true; } - -immutable(short[]) generateDepthInfo(const Token[] tokens) pure nothrow @trusted -{ - import std.exception : assumeUnique; - - short[] retVal = new short[](tokens.length); - short depth = 0; - foreach (i, ref t; tokens) - { - switch (t.type) - { - case tok!"[": - depth++; - goto case; - case tok!"{": - case tok!"(": - depth++; - break; - case tok!"]": - depth--; - goto case; - case tok!"}": - case tok!")": - depth--; - break; - default: - break; - } - retVal[i] = depth; - } - return cast(immutable) retVal; -} - -struct TokenFormatter(OutputRange) -{ - /** - * Params: - * rawSource = ? - * tokens = the tokens to format - * depths = ? - * output = the output range that the code will be formatted to - * astInformation = information about the AST used to inform formatting - * decisions. - * config = ? - */ - this(const ubyte[] rawSource, const(Token)[] tokens, immutable short[] depths, - OutputRange output, ASTInformation* astInformation, Config* config) - { - this.rawSource = rawSource; - this.tokens = tokens; - this.depths = depths; - this.output = output; - this.astInformation = astInformation; - this.config = config; - this.indents = IndentStack(config); - - { - auto eol = config.end_of_line; - if (eol == eol.cr) - this.eolString = "\r"; - else if (eol == eol.lf) - this.eolString = "\n"; - else if (eol == eol.crlf) - this.eolString = "\r\n"; - else if (eol == eol._unspecified) - assert(false, "config.end_of_line was unspecified"); - else - { - assert (eol == eol._default); - this.eolString = eolStringFromInput; - } - } - } - - /// Runs the formatting process - void format() - { - while (hasCurrent) - formatStep(); - } - -private: - - /// Current indentation level - int indentLevel; - - /// Current index into the tokens array - size_t index; - - /// Length of the current line (so far) - uint currentLineLength = 0; - - /// Output to write output to - OutputRange output; - - /// Used for skipping parts of the file with `dfmt off` and `dfmt on` comments - const ubyte[] rawSource; - - /// Tokens being formatted - const Token[] tokens; - - /// Paren depth info - immutable short[] depths; - - /// Information about the AST - const ASTInformation* astInformation; - - /// Token indices where line breaks should be placed - size_t[] linebreakHints; - - /// Current indentation stack for the file - IndentStack indents; - - /// Configuration - const Config* config; - - /// chached end of line string - const string eolString; - - /// Keep track of whether or not an extra newline was just added because of - /// an import statement. - bool justAddedExtraNewline; - - /// Current paren depth - int parenDepth; - - /// Current special brace depth. Used for struct initializers and lambdas. - int sBraceDepth; - - /// Current non-indented brace depth. Used for struct initializers and lambdas. - int niBraceDepth; - - /// True if a space should be placed when parenDepth reaches zero - bool spaceAfterParens; - - /// True if we're in an ASM block - bool inAsm; - - /// True if the next "this" should have a space behind it - bool thisSpace; - - /// True if the next "else" should be formatted as a single line - bool inlineElse; - - /// Tracks paren depth on a single line. This information can be used to - /// indent array literals inside parens, since arrays are indented only once - /// and paren indentation is ignored. Line breaks and "[" reset the counter. - int parenDepthOnLine; - - string eolStringFromInput() const - { - import std.algorithm : countUntil; - - // Intentional wraparound, -1 turns into uint.max when not found: - const firstCR = cast(uint) rawSource.countUntil("\r"); - if (firstCR < cast(uint) rawSource.countUntil("\n")) - return firstCR == rawSource.countUntil("\r\n") ? "\r\n" : "\r"; - return "\n"; - } - - void formatStep() - { - import std.range : assumeSorted; - - assert(hasCurrent); - if (currentIs(tok!"comment")) - { - formatComment(); - } - else if (isStringLiteral(current.type) - || isNumberLiteral(current.type) || currentIs(tok!"characterLiteral")) - { - writeToken(); - if (hasCurrent) - { - immutable t = tokens[index].type; - if (t == tok!"identifier" || isStringLiteral(t) - || isNumberLiteral(t) || t == tok!"characterLiteral" - // a!"b" function() - || t == tok!"function" || t == tok!"delegate") - write(" "); - } - } - else if (currentIs(tok!"module") || currentIs(tok!"import")) - { - formatModuleOrImport(); - } - else if (currentIs(tok!"return")) - { - writeToken(); - if (hasCurrent && (!currentIs(tok!";") && !currentIs(tok!")") && !currentIs(tok!"{") - && !currentIs(tok!"in") && !currentIs(tok!"out") && !currentIs(tok!"do") - && tokens[index].text != "body")) - write(" "); - } - else if (currentIs(tok!"with")) - { - if (indents.length == 0 || !indents.topIsOneOf(tok!"switch", tok!"with")) - indents.push(tok!"with"); - writeToken(); - if (config.dfmt_space_after_keywords) { - write(" "); - } - if (hasCurrent && currentIs(tok!"(")) - writeParens(false); - if (hasCurrent && !currentIs(tok!"switch") && !currentIs(tok!"with") - && !currentIs(tok!"{") && !(currentIs(tok!"final") && peekIs(tok!"switch"))) - { - newline(); - } - else if (hasCurrent && !currentIs(tok!"{")) - { - write(" "); - } - } - else if (currentIs(tok!"switch")) - { - formatSwitch(); - } - else if (currentIs(tok!"extern") && peekIs(tok!"(")) - { - writeToken(); - write(" "); - while (hasCurrent) - { - if (currentIs(tok!"(")) - formatLeftParenOrBracket(); - else if (currentIs(tok!")")) - { - formatRightParen(); - break; - } - else - writeToken(); - } - } - else if (((isBlockHeader() || currentIs(tok!"version")) && peekIs(tok!"(")) - || (currentIs(tok!"debug") && peekIs(tok!"{"))) - { - if (!assumeSorted(astInformation.constraintLocations).equalRange(current.index).empty) - formatConstraint(); - else - formatBlockHeader(); - } - else if ((current.text == "body" || current == tok!"do") && peekBackIsFunctionDeclarationEnding()) - { - formatKeyword(); - } - else if (currentIs(tok!"do")) - { - formatBlockHeader(); - } - else if (currentIs(tok!"else")) - { - formatElse(); - } - else if (currentIs(tok!"asm")) - { - formatKeyword(); - while (hasCurrent && !currentIs(tok!"{")) - formatStep(); - if (hasCurrent) - { - int depth = 1; - formatStep(); - inAsm = true; - while (hasCurrent && depth > 0) - { - if (currentIs(tok!"{")) - ++depth; - else if (currentIs(tok!"}")) - --depth; - formatStep(); - } - inAsm = false; - } - } - else if (currentIs(tok!"this")) - { - const thisIndex = current.index; - formatKeyword(); - if (config.dfmt_space_before_function_parameters - && (thisSpace || astInformation.constructorDestructorLocations - .canFindIndex(thisIndex))) - { - write(" "); - thisSpace = false; - } - } - else if (isKeyword(current.type)) - { - if (currentIs(tok!"debug")) - inlineElse = true; - formatKeyword(); - } - else if (isBasicType(current.type)) - { - writeToken(); - if (hasCurrent && (currentIs(tok!"identifier") || isKeyword(current.type) || inAsm)) - write(" "); - } - else if (isOperator(current.type)) - { - formatOperator(); - } - else if (currentIs(tok!"identifier")) - { - writeToken(); - //dfmt off - if (hasCurrent && ( currentIs(tok!"identifier") - || ( index > 1 && config.dfmt_space_before_function_parameters - && ( isBasicType(peekBack(2).type) - || peekBack2Is(tok!"identifier") - || peekBack2Is(tok!")") - || peekBack2Is(tok!"]") ) - && currentIs(tok!("(") ) - || isBasicType(current.type) || currentIs(tok!"@") - || isNumberLiteral(tokens[index].type) - || (inAsm && peekBack2Is(tok!";") && currentIs(tok!"[")) - ))) - //dfmt on - { - write(" "); - } - } - else if (currentIs(tok!"scriptLine") || currentIs(tok!"specialTokenSequence")) - { - writeToken(); - newline(); - } - else - writeToken(); - } - - void formatConstraint() - { - import dfmt.editorconfig : OB = OptionalBoolean; - with (TemplateConstraintStyle) final switch (config.dfmt_template_constraint_style) - { - case _unspecified: - assert(false, "Config was not validated properly"); - case conditional_newline: - immutable l = currentLineLength + betweenParenLength(tokens[index + 1 .. $]); - if (l > config.dfmt_soft_max_line_length) - newline(); - else if (peekBackIs(tok!")") || peekBackIs(tok!"identifier")) - write(" "); - break; - case always_newline: - newline(); - break; - case conditional_newline_indent: - immutable l = currentLineLength + betweenParenLength(tokens[index + 1 .. $]); - if (l > config.dfmt_soft_max_line_length) - { - config.dfmt_single_template_constraint_indent == OB.t ? - pushWrapIndent() : pushWrapIndent(tok!"!"); - newline(); - } - else if (peekBackIs(tok!")") || peekBackIs(tok!"identifier")) - write(" "); - break; - case always_newline_indent: - { - config.dfmt_single_template_constraint_indent == OB.t ? - pushWrapIndent() : pushWrapIndent(tok!"!"); - newline(); - } - break; - } - // if - writeToken(); - // assume that the parens are present, otherwise the parser would not - // have told us there was a constraint here - write(" "); - writeParens(false); - } - - string commentText(size_t i) - { - import std.string : strip; - - assert(tokens[i].type == tok!"comment"); - string commentText = tokens[i].text; - if (commentText[0 .. 2] == "//") - commentText = commentText[2 .. $]; - else - { - if (commentText.length > 3) - commentText = commentText[2 .. $ - 2]; - else - commentText = commentText[2 .. $]; - } - return commentText.strip(); - } - - void skipFormatting() - { - size_t dfmtOff = index; - size_t dfmtOn = index; - foreach (i; dfmtOff + 1 .. tokens.length) - { - dfmtOn = i; - if (tokens[i].type != tok!"comment") - continue; - immutable string commentText = commentText(i); - if (commentText == "dfmt on") - break; - } - write(cast(string) rawSource[tokens[dfmtOff].index .. tokens[dfmtOn].index]); - index = dfmtOn; - } - - void formatComment() - { - if (commentText(index) == "dfmt off") - { - skipFormatting(); - return; - } - - immutable bool currIsSlashSlash = tokens[index].text[0 .. 2] == "//"; - immutable prevTokenEndLine = index == 0 ? size_t.max : tokenEndLine(tokens[index - 1]); - immutable size_t currTokenLine = tokens[index].line; - if (index > 0) - { - immutable t = tokens[index - 1].type; - immutable canAddNewline = currTokenLine - prevTokenEndLine < 1; - if (peekBackIsOperator() && !isSeparationToken(t)) - pushWrapIndent(t); - else if (peekBackIs(tok!",") && prevTokenEndLine == currTokenLine - && indents.indentToMostRecent(tok!"enum") == -1) - pushWrapIndent(tok!","); - if (peekBackIsOperator() && !peekBackIsOneOf(false, tok!"comment", - tok!"{", tok!"}", tok!":", tok!";", tok!",", tok!"[", tok!"(") - && !canAddNewline && prevTokenEndLine < currTokenLine) - write(" "); - else if (prevTokenEndLine == currTokenLine || (t == tok!")" && peekIs(tok!"{"))) - write(" "); - else if (peekBackIsOneOf(false, tok!"else", tok!"identifier")) - write(" "); - else if (canAddNewline || (peekIs(tok!"{") && t == tok!"}")) - newline(); - - if (peekIs(tok!"(") && (peekBackIs(tok!")") || peekBack2Is(tok!"!"))) - pushWrapIndent(tok!"("); - - if (peekIs(tok!".") && !indents.topIs(tok!".")) - indents.push(tok!"."); - } - writeToken(); - immutable j = justAddedExtraNewline; - if (currIsSlashSlash) - { - newline(); - justAddedExtraNewline = j; - } - else if (hasCurrent) - { - if (prevTokenEndLine == tokens[index].line) - { - if (currentIs(tok!"}")) - { - if (indents.topIs(tok!"{")) - indents.pop(); - write(" "); - } - else if (!currentIs(tok!"{")) - write(" "); - } - else if (!currentIs(tok!"{") && !currentIs(tok!"in") && !currentIs(tok!"out")) - { - if (currentIs(tok!")") && indents.topIs(tok!",")) - indents.pop(); - else if (peekBack2Is(tok!",") && !indents.topIs(tok!",") - && indents.indentToMostRecent(tok!"enum") == -1) - pushWrapIndent(tok!","); - newline(); - } - } - else - newline(); - } - - void formatModuleOrImport() - { - immutable t = current.type; - writeToken(); - if (currentIs(tok!"(")) - { - writeParens(false); - return; - } - write(" "); - while (hasCurrent) - { - if (currentIs(tok!";")) - { - indents.popWrapIndents(); - indentLevel = indents.indentLevel; - writeToken(); - if (index >= tokens.length) - { - newline(); - break; - } - if (currentIs(tok!"comment") && current.line == peekBack().line) - { - break; - } - else if (currentIs(tok!"{") && config.dfmt_brace_style == BraceStyle.allman) - break; - else if (t == tok!"import" && !currentIs(tok!"import") - && !currentIs(tok!"}") - && !((currentIs(tok!"public") - || currentIs(tok!"private") - || currentIs(tok!"static")) - && peekIs(tok!"import")) && !indents.topIsOneOf(tok!"if", - tok!"debug", tok!"version")) - { - simpleNewline(); - currentLineLength = 0; - justAddedExtraNewline = true; - newline(); - } - else - newline(); - break; - } - else if (currentIs(tok!":")) - { - if (config.dfmt_selective_import_space) - write(" "); - writeToken(); - if (!currentIs(tok!"comment")) - write(" "); - pushWrapIndent(tok!","); - } - else if (currentIs(tok!"comment")) - { - if (peekBack.line != current.line) - { - // The comment appears on its own line, keep it there. - if (!peekBackIs(tok!"comment")) - // Comments are already properly separated. - newline(); - } - formatStep(); - } - else - formatStep(); - } - } - - void formatLeftParenOrBracket() - in - { - assert(currentIs(tok!"(") || currentIs(tok!"[")); - } - do - { - import dfmt.editorconfig : OptionalBoolean; - - immutable p = current.type; - regenLineBreakHintsIfNecessary(index); - writeToken(); - if (p == tok!"(") - { - ++parenDepthOnLine; - // If the file starts with an open paren, just give up. This isn't - // valid D code. - if (index < 2) - return; - if (isBlockHeaderToken(tokens[index - 2].type)) - indents.push(tok!")"); - else - indents.push(p); - spaceAfterParens = true; - parenDepth++; - } - // No heuristics apply if we can't look before the opening paren/bracket - if (index < 1) - return; - immutable bool arrayInitializerStart = p == tok!"[" - && astInformation.arrayStartLocations.canFindIndex(tokens[index - 1].index); - - if (arrayInitializerStart && isMultilineAt(index - 1)) - { - revertParenIndentation(); - - // Use the close bracket as the indent token to distinguish - // the array initialiazer from an array index in the newline - // handling code - IndentStack.Details detail; - detail.wrap = false; - detail.temp = false; - - // wrap and temp are set manually to the values it would actually - // receive here because we want to set breakEveryItem for the ] token to know if - // we should definitely always new-line after every comma for a big AA - detail.breakEveryItem = astInformation.assocArrayStartLocations.canFindIndex( - tokens[index - 1].index); - detail.preferLongBreaking = true; - - indents.push(tok!"]", detail); - newline(); - immutable size_t j = expressionEndIndex(index); - linebreakHints = chooseLineBreakTokens(index, tokens[index .. j], - depths[index .. j], config, currentLineLength, indentLevel); - } - else if (p == tok!"[" && config.dfmt_keep_line_breaks == OptionalBoolean.t) - { - revertParenIndentation(); - IndentStack.Details detail; - - detail.wrap = false; - detail.temp = false; - detail.breakEveryItem = false; - detail.mini = tokens[index].line == tokens[index - 1].line; - - indents.push(tok!"]", detail); - if (!detail.mini) - { - newline(); - } - } - else if (arrayInitializerStart) - { - // This is a short (non-breaking) array/AA value - IndentStack.Details detail; - detail.wrap = false; - detail.temp = false; - - detail.breakEveryItem = astInformation.assocArrayStartLocations.canFindIndex(tokens[index - 1].index); - // array of (possibly associative) array, let's put each item on its own line - if (!detail.breakEveryItem && currentIs(tok!"[")) - detail.breakEveryItem = true; - - // the '[' is immediately followed by an item instead of a newline here so - // we set mini, that the ']' also follows an item immediately without newline. - detail.mini = true; - - indents.push(tok!"]", detail); - } - else if (p == tok!"[") - { - // array item access - IndentStack.Details detail; - detail.wrap = false; - detail.temp = true; - detail.mini = true; - indents.push(tok!"]", detail); - } - else if (!currentIs(tok!")") && !currentIs(tok!"]") - && (linebreakHints.canFindIndex(index - 1) || (linebreakHints.length == 0 - && currentLineLength > config.max_line_length))) - { - newline(); - } - else if (onNextLine) - { - newline(); - } - } - - void revertParenIndentation() - { - import std.algorithm.searching : canFind, until; - - if (tokens[index .. $].until!(tok => tok.line != current.line).canFind!(x => x.type == tok!"]")) - { - return; - } - if (parenDepthOnLine) - { - foreach (i; 0 .. parenDepthOnLine) - { - indents.pop(); - } - } - parenDepthOnLine = 0; - } - - void formatRightParen() - in - { - assert(currentIs(tok!")")); - } - do - { - parenDepthOnLine = max(parenDepthOnLine - 1, 0); - parenDepth--; - indents.popWrapIndents(); - while (indents.topIsOneOf(tok!"!", tok!")")) - indents.pop(); - if (indents.topIs(tok!"(")) - indents.pop(); - if (indents.topIs(tok!".")) - indents.pop(); - - if (onNextLine) - { - newline(); - } - if (parenDepth == 0 && (peekIs(tok!"is") || peekIs(tok!"in") - || peekIs(tok!"out") || peekIs(tok!"do") || peekIsBody)) - { - writeToken(); - } - else if (peekIsLiteralOrIdent() || peekIsBasicType()) - { - writeToken(); - if (spaceAfterParens || parenDepth > 0) - writeSpace(); - } - else if ((peekIsKeyword() || peekIs(tok!"@")) && spaceAfterParens - && !peekIs(tok!"in") && !peekIs(tok!"is") && !peekIs(tok!"if")) - { - writeToken(); - writeSpace(); - } - else - writeToken(); - } - - void formatRightBracket() - in - { - assert(currentIs(tok!"]")); - } - do - { - indents.popWrapIndents(); - if (indents.topIs(tok!"]")) - { - if (!indents.topDetails.mini && !indents.topDetails.temp) - newline(); - else - indents.pop(); - } - writeToken(); - if (currentIs(tok!"identifier")) - write(" "); - } - - void formatAt() - { - immutable size_t atIndex = tokens[index].index; - writeToken(); - if (currentIs(tok!"identifier")) - writeToken(); - if (currentIs(tok!"(")) - { - writeParens(false); - if (tokens[index].type == tok!"{") - return; - - if (hasCurrent && tokens[index - 1].line < tokens[index].line - && astInformation.atAttributeStartLocations.canFindIndex(atIndex)) - newline(); - else - write(" "); - } - else if (hasCurrent && (currentIs(tok!"@") - || isBasicType(tokens[index].type) - || currentIs(tok!"invariant") - || currentIs(tok!"extern") - || currentIs(tok!"identifier")) - && !currentIsIndentedTemplateConstraint()) - { - writeSpace(); - } - } - - void formatColon() - { - import dfmt.editorconfig : OptionalBoolean; - import std.algorithm : canFind, any; - immutable bool isCase = astInformation.caseEndLocations.canFindIndex(current.index); - immutable bool isAttribute = astInformation.attributeDeclarationLines.canFindIndex( - current.line); - immutable bool isStructInitializer = astInformation.structInfoSortedByEndLocation - .canFind!(st => st.startLocation < current.index && current.index < st.endLocation); - immutable bool isTernary = astInformation.ternaryColonLocations.canFindIndex(current.index); - immutable bool isNamedArg = astInformation.namedArgumentColonLocations.canFindIndex(current.index); - - if (isCase || isAttribute) - { - writeToken(); - if (!currentIs(tok!"{")) - { - if (isCase && !indents.topIs(tok!"case") - && config.dfmt_align_switch_statements == OptionalBoolean.f) - indents.push(tok!"case"); - else if (isAttribute && !indents.topIs(tok!"@") - && config.dfmt_outdent_attributes == OptionalBoolean.f) - indents.push(tok!"@"); - newline(); - } - } - else if (indents.topIs(tok!"]") && !isTernary) // Associative array - { - write(config.dfmt_space_before_aa_colon ? " : " : ": "); - ++index; - } - // Named function or struct constructor arguments. - else if (isNamedArg) - { - write(config.dfmt_space_before_named_arg_colon ? " : " : ": "); - ++index; - } - else if (peekBackIs(tok!"identifier") - && [tok!"{", tok!"}", tok!";", tok!":", tok!","] - .any!((ptrdiff_t token) => peekBack2Is(cast(IdType)token, true)) - && (!isBlockHeader(1) || peekIs(tok!"if"))) - { - writeToken(); - if (isStructInitializer) - write(" "); - else if (!currentIs(tok!"{")) - newline(); - } - else - { - regenLineBreakHintsIfNecessary(index); - if (peekIs(tok!"..")) - writeToken(); - else if (isBlockHeader(1) && !peekIs(tok!"if")) - { - writeToken(); - if (config.dfmt_compact_labeled_statements) - write(" "); - else - newline(); - } - else if (linebreakHints.canFindIndex(index)) - { - pushWrapIndent(); - newline(); - writeToken(); - write(" "); - } - else - { - write(" : "); - index++; - } - } - } - - void formatSemicolon() - { - if (inlineElse && !peekIs(tok!"else")) - inlineElse = false; - - if ((parenDepth > 0 && sBraceDepth == 0) || (sBraceDepth > 0 && niBraceDepth > 0)) - { - if (currentLineLength > config.dfmt_soft_max_line_length) - { - writeToken(); - pushWrapIndent(tok!";"); - newline(); - } - else - { - if (!(peekIs(tok!";") || peekIs(tok!")") || peekIs(tok!"}"))) - write("; "); - else - write(";"); - index++; - } - } - else - { - writeToken(); - indents.popWrapIndents(); - linebreakHints = []; - while (indents.topIsOneOf(tok!"enum", tok!"try", tok!"catch", tok!"finally", tok!"debug")) - indents.pop(); - if (indents.topAre(tok!"static", tok!"else")) - { - indents.pop(); - indents.pop(); - } - indentLevel = indents.indentLevel; - if (config.dfmt_brace_style == BraceStyle.allman) - { - if (!currentIs(tok!"{")) - newline(); - } - else - { - if (currentIs(tok!"{")) - indents.popTempIndents(); - indentLevel = indents.indentLevel; - newline(); - } - } - } - - void formatLeftBrace() - { - import std.algorithm : map, sum, canFind; - - auto tIndex = tokens[index].index; - - if (astInformation.structInitStartLocations.canFindIndex(tIndex)) - { - sBraceDepth++; - immutable bool multiline = isMultilineAt(index); - writeToken(); - if (multiline) - { - import std.algorithm.searching : find; - - auto indentInfo = astInformation.indentInfoSortedByEndLocation - .find!((a,b) => a.startLocation == b)(tIndex); - assert(indentInfo.length > 0); - cast()indentInfo[0].flags |= BraceIndentInfoFlags.tempIndent; - cast()indentInfo[0].beginIndentLevel = indents.indentLevel; - - indents.push(tok!"{"); - newline(); - } - else - niBraceDepth++; - } - else if (astInformation.funLitStartLocations.canFindIndex(tIndex)) - { - indents.popWrapIndents(); - - sBraceDepth++; - if (peekBackIsOneOf(true, tok!")", tok!"identifier")) - write(" "); - immutable bool multiline = isMultilineAt(index); - writeToken(); - if (multiline) - { - indents.push(tok!"{"); - newline(); - } - else - { - niBraceDepth++; - if (!currentIs(tok!"}")) - write(" "); - } - } - else - { - if (peekBackIsSlashSlash()) - { - if (peekBack2Is(tok!";")) - { - indents.popTempIndents(); - indentLevel = indents.indentLevel - 1; - } - writeToken(); - } - else - { - if (indents.topIsTemp && indents.indentToMostRecent(tok!"static") == -1) - indentLevel = indents.indentLevel - 1; - else - indentLevel = indents.indentLevel; - if (config.dfmt_brace_style == BraceStyle.allman - || peekBackIsOneOf(true, tok!"{", tok!"}")) - newline(); - else if (config.dfmt_brace_style == BraceStyle.knr - && astInformation.funBodyLocations.canFindIndex(tIndex) - && (peekBackIs(tok!")") || (!peekBackIs(tok!"do") && peekBack().text != "body"))) - newline(); - else if (!peekBackIsOneOf(true, tok!"{", tok!"}", tok!";")) - write(" "); - writeToken(); - } - indents.push(tok!"{"); - if (!currentIs(tok!"{")) - newline(); - linebreakHints = []; - } - } - - void formatRightBrace() - { - void popToBeginIndent(BraceIndentInfo indentInfo) - { - foreach(i; indentInfo.beginIndentLevel .. indents.indentLevel) - { - indents.pop(); - } - - indentLevel = indentInfo.beginIndentLevel; - } - - size_t pos; - if (astInformation.structInitEndLocations.canFindIndex(tokens[index].index, &pos)) - { - if (sBraceDepth > 0) - sBraceDepth--; - if (niBraceDepth > 0) - niBraceDepth--; - - // Account for possible function literals in this array which offset - // the previously set index (pos). Fixes issue #432. - size_t newPos = pos; - while(astInformation.indentInfoSortedByEndLocation[newPos].endLocation < - tokens[index].index) - { - newPos++; - } - - if (astInformation.indentInfoSortedByEndLocation[newPos].endLocation == - tokens[index].index) - { - pos = newPos; - } - - auto indentInfo = astInformation.indentInfoSortedByEndLocation[pos]; - if (indentInfo.flags & BraceIndentInfoFlags.tempIndent) - { - popToBeginIndent(indentInfo); - simpleNewline(); - indent(); - } - writeToken(); - } - else if (astInformation.funLitEndLocations.canFindIndex(tokens[index].index, &pos)) - { - if (niBraceDepth > 0) - { - if (!peekBackIsSlashSlash() && !peekBackIs(tok!"{")) - write(" "); - niBraceDepth--; - } - if (sBraceDepth > 0) - sBraceDepth--; - writeToken(); - } - else - { - // Silly hack to format enums better. - if ((peekBackIsLiteralOrIdent() || peekBackIsOneOf(true, tok!")", - tok!",")) && !peekBackIsSlashSlash()) - newline(); - write("}"); - if (index + 1 < tokens.length - && astInformation.doubleNewlineLocations.canFindIndex(tokens[index].index) - && !peekIs(tok!"}") && !peekIs(tok!"else") - && !peekIs(tok!";") && !peekIs(tok!"comment", false)) - { - simpleNewline(); - currentLineLength = 0; - justAddedExtraNewline = true; - } - if (config.dfmt_brace_style.among(BraceStyle.otbs, BraceStyle.knr) - && ((peekIs(tok!"else") - && !indents.topAre(tok!"static", tok!"if") - && !indents.topIs(tok!"foreach") && !indents.topIs(tok!"for") - && !indents.topIs(tok!"while") && !indents.topIs(tok!"do")) - || peekIs(tok!"catch") || peekIs(tok!"finally"))) - { - write(" "); - index++; - } - else - { - if (!peekIs(tok!",") && !peekIs(tok!")") - && !peekIs(tok!";") && !peekIs(tok!"{")) - { - index++; - if (indents.topIs(tok!"static")) - indents.pop(); - newline(); - } - else - index++; - } - } - } - - void formatSwitch() - { - while (indents.topIs(tok!"with")) - indents.pop(); - indents.push(tok!"switch"); - writeToken(); // switch - if (config.dfmt_space_after_keywords) - { - write(" "); - } - } - - void formatBlockHeader() - { - if (indents.topIs(tok!"!")) - indents.pop(); - immutable bool a = !currentIs(tok!"version") && !currentIs(tok!"debug"); - immutable bool b = a - || astInformation.conditionalWithElseLocations.canFindIndex(current.index); - immutable bool c = b - || astInformation.conditionalStatementLocations.canFindIndex(current.index); - immutable bool shouldPushIndent = (c || peekBackIs(tok!"else")) - && !(currentIs(tok!"if") && indents.topIsWrap()); - if (currentIs(tok!"out") && !peekBackIs(tok!"}")) - newline(); - if (shouldPushIndent) - { - if (peekBackIs(tok!"static")) - { - if (indents.topIs(tok!"else")) - indents.pop(); - if (!indents.topIs(tok!"static")) - indents.push(tok!"static"); - } - indents.push(current.type); - } - writeToken(); - - if (currentIs(tok!"(")) - { - if (config.dfmt_space_after_keywords) - { - write(" "); - } - writeParens(false); - } - - if (hasCurrent) - { - if (currentIs(tok!"switch") || (currentIs(tok!"final") && peekIs(tok!"switch"))) - { - if (config.dfmt_space_after_keywords) - { - write(" "); - } - } - else if (currentIs(tok!"comment")) - { - formatStep(); - } - else if (!shouldPushIndent) - { - if (!currentIs(tok!"{") && !currentIs(tok!";")) - write(" "); - } - else if (hasCurrent && !currentIs(tok!"{") && !currentIs(tok!";") && !currentIs(tok!"in") && - !currentIs(tok!"out") && !currentIs(tok!"do") && current.text != "body") - { - newline(); - } - else if (currentIs(tok!"{") && indents.topAre(tok!"static", tok!"if")) - { - // Hacks to format braced vs non-braced static if declarations. - indents.pop(); - indents.pop(); - indents.push(tok!"if"); - formatLeftBrace(); - } - else if (currentIs(tok!"{") && indents.topAre(tok!"static", tok!"foreach")) - { - indents.pop(); - indents.pop(); - indents.push(tok!"foreach"); - formatLeftBrace(); - } - else if (currentIs(tok!"{") && indents.topAre(tok!"static", tok!"foreach_reverse")) - { - indents.pop(); - indents.pop(); - indents.push(tok!"foreach_reverse"); - formatLeftBrace(); - } - } - } - - void formatElse() - { - writeToken(); - if (inlineElse || currentIs(tok!"if") || currentIs(tok!"version") - || (currentIs(tok!"static") && peekIs(tok!"if"))) - { - if (indents.topIs(tok!"if") || indents.topIs(tok!"version")) - indents.pop(); - inlineElse = false; - write(" "); - } - else if (currentIs(tok!":")) - { - writeToken(); - newline(); - } - else if (!currentIs(tok!"{") && !currentIs(tok!"comment")) - { - //indents.dump(); - while (indents.topIsOneOf(tok!"foreach", tok!"for", tok!"while")) - indents.pop(); - if (indents.topIsOneOf(tok!"if", tok!"version")) - indents.pop(); - indents.push(tok!"else"); - newline(); - } - else if (currentIs(tok!"{") && indents.topAre(tok!"static", tok!"if")) - { - indents.pop(); - indents.pop(); - indents.push(tok!"else"); - } - } - - void formatKeyword() - { - import dfmt.editorconfig : OptionalBoolean; - - switch (current.type) - { - case tok!"default": - writeToken(); - break; - case tok!"cast": - writeToken(); - if (hasCurrent && currentIs(tok!"(")) - writeParens(config.dfmt_space_after_cast == OptionalBoolean.t); - break; - case tok!"out": - if (!peekBackIsSlashSlash) { - if (!peekBackIs(tok!"}") - && astInformation.contractLocations.canFindIndex(current.index)) - newline(); - else if (peekBackIsKeyword) - write(" "); - } - writeToken(); - if (hasCurrent && !currentIs(tok!"{") && !currentIs(tok!"comment")) - write(" "); - break; - case tok!"try": - case tok!"finally": - indents.push(current.type); - writeToken(); - if (hasCurrent && !currentIs(tok!"{")) - newline(); - break; - case tok!"identifier": - if (current.text == "body") - goto case tok!"do"; - else - goto default; - case tok!"do": - if (!peekBackIs(tok!"}")) - newline(); - writeToken(); - break; - case tok!"in": - immutable isContract = astInformation.contractLocations.canFindIndex(current.index); - if (!peekBackIsSlashSlash) { - if (isContract) - { - indents.popTempIndents(); - newline(); - } - else if (!peekBackIsOneOf(false, tok!"(", tok!",", tok!"!")) - write(" "); - } - writeToken(); - if (!hasCurrent) - return; - immutable isFunctionLit = astInformation.funLitStartLocations.canFindIndex( - current.index); - if (isFunctionLit && config.dfmt_brace_style == BraceStyle.allman) - newline(); - else if (!isContract || currentIs(tok!"(")) - write(" "); - break; - case tok!"is": - if (!peekBackIsOneOf(false, tok!"!", tok!"(", tok!",", - tok!"}", tok!"=", tok!"&&", tok!"||") && !peekBackIsKeyword()) - write(" "); - writeToken(); - if (hasCurrent && !currentIs(tok!"(") && !currentIs(tok!"{") && !currentIs(tok!"comment")) - write(" "); - break; - case tok!"case": - writeToken(); - if (hasCurrent && !currentIs(tok!";")) - write(" "); - break; - case tok!"enum": - if (peekIs(tok!")") || peekIs(tok!"==")) - { - writeToken(); - } - else - { - if (peekBackIs(tok!"identifier")) - write(" "); - indents.push(tok!"enum"); - writeToken(); - if (hasCurrent && !currentIs(tok!":") && !currentIs(tok!"{")) - write(" "); - } - break; - case tok!"static": - { - if (astInformation.staticConstructorDestructorLocations - .canFindIndex(current.index)) - { - thisSpace = true; - } - } - goto default; - case tok!"shared": - { - if (astInformation.sharedStaticConstructorDestructorLocations - .canFindIndex(current.index)) - { - thisSpace = true; - } - } - goto default; - case tok!"invariant": - writeToken(); - if (hasCurrent && currentIs(tok!"(")) - write(" "); - break; - default: - if (peekBackIs(tok!"identifier")) - { - writeSpace(); - } - if (index + 1 < tokens.length) - { - if (!peekIs(tok!"@") && (peekIsOperator() - || peekIs(tok!"out") || peekIs(tok!"in"))) - { - writeToken(); - } - else - { - writeToken(); - if (!currentIsIndentedTemplateConstraint()) - { - writeSpace(); - } - } - } - else - writeToken(); - break; - } - } - - bool currentIsIndentedTemplateConstraint() - { - return hasCurrent - && astInformation.constraintLocations.canFindIndex(current.index) - && (config.dfmt_template_constraint_style == TemplateConstraintStyle.always_newline - || config.dfmt_template_constraint_style == TemplateConstraintStyle.always_newline_indent - || currentLineLength >= config.dfmt_soft_max_line_length); - } - - void formatOperator() - { - import dfmt.editorconfig : OptionalBoolean; - import std.algorithm : canFind; - - switch (current.type) - { - case tok!"*": - if (astInformation.spaceAfterLocations.canFindIndex(current.index)) - { - writeToken(); - if (!currentIs(tok!"*") && !currentIs(tok!")") - && !currentIs(tok!"[") && !currentIs(tok!",") && !currentIs(tok!";")) - { - write(" "); - } - break; - } - else if (astInformation.unaryLocations.canFindIndex(current.index)) - { - writeToken(); - break; - } - regenLineBreakHintsIfNecessary(index); - goto binary; - case tok!"~": - if (peekIs(tok!"this") && peek2Is(tok!"(")) - { - if (!(index == 0 || peekBackIs(tok!"{", true) - || peekBackIs(tok!"}", true) || peekBackIs(tok!";", true))) - { - write(" "); - } - writeToken(); - break; - } - goto case; - case tok!"&": - case tok!"+": - case tok!"-": - if (astInformation.unaryLocations.canFindIndex(current.index)) - { - writeToken(); - break; - } - regenLineBreakHintsIfNecessary(index); - goto binary; - case tok!"[": - case tok!"(": - formatLeftParenOrBracket(); - break; - case tok!")": - formatRightParen(); - break; - case tok!"@": - formatAt(); - break; - case tok!"!": - if (((peekIs(tok!"is") || peekIs(tok!"in")) - && !peekBackIsOperator()) || peekBackIs(tok!")")) - write(" "); - goto case; - case tok!"...": - case tok!"++": - case tok!"--": - case tok!"$": - writeToken(); - break; - case tok!":": - formatColon(); - break; - case tok!"]": - formatRightBracket(); - break; - case tok!";": - formatSemicolon(); - break; - case tok!"{": - formatLeftBrace(); - break; - case tok!"}": - formatRightBrace(); - break; - case tok!".": - regenLineBreakHintsIfNecessary(index); - immutable bool ufcsWrap = config.dfmt_reflow_property_chains == OptionalBoolean.t - && astInformation.ufcsHintLocations.canFindIndex(current.index); - if (ufcsWrap || linebreakHints.canFind(index) || onNextLine - || (linebreakHints.length == 0 && currentLineLength + nextTokenLength() > config.max_line_length)) - { - if (!indents.topIs(tok!".")) - indents.push(tok!"."); - if (!peekBackIs(tok!"comment")) - newline(); - if (ufcsWrap || onNextLine) - regenLineBreakHints(index); - } - writeToken(); - break; - case tok!",": - formatComma(); - break; - case tok!"&&": - case tok!"||": - case tok!"|": - regenLineBreakHintsIfNecessary(index); - goto case; - case tok!"=": - case tok!">=": - case tok!">>=": - case tok!">>>=": - case tok!"|=": - case tok!"-=": - case tok!"/=": - case tok!"*=": - case tok!"&=": - case tok!"%=": - case tok!"+=": - case tok!"^^": - case tok!"^=": - case tok!"^": - case tok!"~=": - case tok!"<<=": - case tok!"<<": - case tok!"<=": - case tok!"<>=": - case tok!"<>": - case tok!"<": - case tok!"==": - case tok!"=>": - case tok!">>>": - case tok!">>": - case tok!">": - case tok!"!<=": - case tok!"!<>=": - case tok!"!<>": - case tok!"!<": - case tok!"!=": - case tok!"!>=": - case tok!"!>": - case tok!"?": - case tok!"/": - case tok!"..": - case tok!"%": - binary: - immutable bool isWrapToken = linebreakHints.canFind(index); - if (config.dfmt_keep_line_breaks == OptionalBoolean.t && index > 0) - { - const operatorLine = tokens[index].line; - const rightOperandLine = tokens[index + 1].line; - - if (tokens[index - 1].line < operatorLine) - { - if (!indents.topIs(tok!"enum")) - pushWrapIndent(); - if (!peekBackIs(tok!"comment")) - newline(); - } - else - { - write(" "); - } - if (rightOperandLine > operatorLine - && !indents.topIs(tok!"enum")) - { - pushWrapIndent(); - } - writeToken(); - - if (rightOperandLine > operatorLine) - { - if (!peekBackIs(tok!"comment")) - newline(); - } - else - { - write(" "); - } - } - else if (config.dfmt_split_operator_at_line_end) - { - if (isWrapToken) - { - if (!indents.topIs(tok!"enum")) - pushWrapIndent(); - write(" "); - writeToken(); - newline(); - } - else - { - write(" "); - writeToken(); - if (!currentIs(tok!"comment")) - write(" "); - } - } - else - { - if (isWrapToken) - { - if (!indents.topIs(tok!"enum")) - pushWrapIndent(); - newline(); - writeToken(); - } - else - { - write(" "); - writeToken(); - } - if (!currentIs(tok!"comment")) - write(" "); - } - break; - default: - writeToken(); - break; - } - } - - void formatComma() - { - import dfmt.editorconfig : OptionalBoolean; - import std.algorithm : canFind; - - if (config.dfmt_keep_line_breaks == OptionalBoolean.f) - regenLineBreakHintsIfNecessary(index); - if (indents.indentToMostRecent(tok!"enum") != -1 - && !peekIs(tok!"}") && indents.topIs(tok!"{") && parenDepth == 0) - { - writeToken(); - newline(); - } - else if (indents.topIs(tok!"]") && indents.topDetails.breakEveryItem - && !indents.topDetails.mini) - { - writeToken(); - newline(); - regenLineBreakHints(index - 1); - } - else if (indents.topIs(tok!"]") && indents.topDetails.preferLongBreaking - && !currentIs(tok!")") && !currentIs(tok!"]") && !currentIs(tok!"}") - && !currentIs(tok!"comment") && index + 1 < tokens.length - && isMultilineAt(index + 1, true)) - { - writeToken(); - newline(); - regenLineBreakHints(index - 1); - } - else if (config.dfmt_keep_line_breaks == OptionalBoolean.t) - { - const commaLine = tokens[index].line; - - writeToken(); - if (indents.topIsWrap && !indents.topIs(tok!",")) - { - indents.pop; - } - if (!currentIs(tok!")") && !currentIs(tok!"]") - && !currentIs(tok!"}") && !currentIs(tok!"comment")) - { - if (tokens[index].line == commaLine) - { - write(" "); - } - else - { - newline(); - } - } - } - else if (!peekIs(tok!"}") && (linebreakHints.canFind(index) - || (linebreakHints.length == 0 && currentLineLength > config.max_line_length))) - { - pushWrapIndent(); - writeToken(); - if (indents.topIsWrap && !indents.topIs(tok!",")) - { - indents.pop; - } - newline(); - } - else - { - writeToken(); - if (!currentIs(tok!")") && !currentIs(tok!"]") - && !currentIs(tok!"}") && !currentIs(tok!"comment")) - { - write(" "); - } - } - regenLineBreakHintsIfNecessary(index - 1); - } - - void regenLineBreakHints(immutable size_t i) - { - import std.range : assumeSorted; - import std.algorithm.comparison : min; - import std.algorithm.searching : canFind, countUntil; - - // The end of the tokens considered by the line break algorithm is - // either the expression end index or the next mandatory line break - // or a newline inside a string literal, whichever is first. - auto r = assumeSorted(astInformation.ufcsHintLocations).upperBound(tokens[i].index); - immutable ufcsBreakLocation = r.empty - ? size_t.max - : tokens[i .. $].countUntil!(t => t.index == r.front) + i; - immutable multilineStringLocation = tokens[i .. $] - .countUntil!(t => t.text.canFind('\n')); - immutable size_t j = min( - expressionEndIndex(i), - ufcsBreakLocation, - multilineStringLocation == -1 ? size_t.max : multilineStringLocation + i + 1); - // Use magical negative value for array literals and wrap indents - immutable inLvl = (indents.topIsWrap() || indents.topIs(tok!"]")) ? -indentLevel - : indentLevel; - linebreakHints = chooseLineBreakTokens(i, tokens[i .. j], depths[i .. j], - config, currentLineLength, inLvl); - } - - void regenLineBreakHintsIfNecessary(immutable size_t i) - { - if (linebreakHints.length == 0 || linebreakHints[$ - 1] <= i - 1) - regenLineBreakHints(i); - } - - void simpleNewline() - { - import dfmt.editorconfig : EOL; - - output.put(eolString); - } - - void newline() - { - import std.range : assumeSorted; - import std.algorithm : max, canFind; - import dfmt.editorconfig : OptionalBoolean; - - if (currentIs(tok!"comment") && index > 0 && current.line == tokenEndLine(tokens[index - 1])) - return; - - immutable bool hasCurrent = this.hasCurrent; - - if (niBraceDepth > 0 && !peekBackIsSlashSlash() && hasCurrent && tokens[index].type == tok!"}" - && !assumeSorted(astInformation.funLitEndLocations).equalRange( - tokens[index].index).empty) - { - return; - } - - simpleNewline(); - - if (!justAddedExtraNewline && index > 0 && hasCurrent - && tokens[index].line - tokenEndLine(tokens[index - 1]) > 1) - { - simpleNewline(); - } - - justAddedExtraNewline = false; - currentLineLength = 0; - - if (hasCurrent) - { - if (currentIs(tok!"else")) - { - immutable i = indents.indentToMostRecent(tok!"if"); - immutable v = indents.indentToMostRecent(tok!"version"); - immutable mostRecent = max(i, v); - if (mostRecent != -1) - indentLevel = mostRecent; - } - else if (currentIs(tok!"identifier") && peekIs(tok!":")) - { - if (peekBackIs(tok!"}", true) || peekBackIs(tok!";", true)) - indents.popTempIndents(); - immutable l = indents.indentToMostRecent(tok!"switch"); - if (l != -1 && config.dfmt_align_switch_statements == OptionalBoolean.t) - indentLevel = l; - else if (astInformation.structInfoSortedByEndLocation - .canFind!(st => st.startLocation < current.index && current.index < st.endLocation)) { - immutable l2 = indents.indentToMostRecent(tok!"{"); - assert(l2 != -1, "Recent '{' is not found despite being in struct initializer"); - indentLevel = l2 + 1; - } - else if ((config.dfmt_compact_labeled_statements == OptionalBoolean.f - || !isBlockHeader(2) || peek2Is(tok!"if")) && !indents.topIs(tok!"]")) - { - immutable l2 = indents.indentToMostRecent(tok!"{"); - indentLevel = l2 != -1 ? l2 : indents.indentLevel - 1; - } - else - indentLevel = indents.indentLevel; - } - else if (currentIs(tok!"case") || currentIs(tok!"default")) - { - - if (peekBackIs(tok!"}", true) || peekBackIs(tok!";", true) - /** - * The following code is valid and should be indented flatly - * case A: - * case B: - */ - || peekBackIs(tok!":", true)) - { - indents.popTempIndents(); - if (indents.topIs(tok!"case")) - indents.pop(); - } - immutable l = indents.indentToMostRecent(tok!"switch"); - if (l != -1) - indentLevel = config.dfmt_align_switch_statements == OptionalBoolean.t - ? l : indents.indentLevel; - } - else if (currentIs(tok!")")) - { - if (indents.topIs(tok!"(")) - indents.pop(); - indentLevel = indents.indentLevel; - } - else if (currentIs(tok!"{")) - { - indents.popWrapIndents(); - if ((peekBackIsSlashSlash() && peekBack2Is(tok!";")) || indents.topIs(tok!"]")) - { - indents.popTempIndents(); - indentLevel = indents.indentLevel; - } - } - else if (currentIs(tok!"}")) - { - indents.popTempIndents(); - while (indents.topIsOneOf(tok!"case", tok!"@", tok!"static")) - indents.pop(); - if (indents.topIs(tok!"{")) - { - indentLevel = indents.indentToMostRecent(tok!"{"); - indents.pop(); - } - if (indents.topIsOneOf(tok!"try", tok!"catch")) - { - indents.pop(); - } - else while (sBraceDepth == 0 && indents.topIsTemp() - && ((!indents.topIsOneOf(tok!"else", tok!"if", - tok!"static", tok!"version")) || !peekIs(tok!"else"))) - { - indents.pop(); - } - } - else if (currentIs(tok!"]")) - { - indents.popWrapIndents(); - if (indents.topIs(tok!"]")) - { - indents.pop(); - } - // Find the initial indentation of constructs like "if" and - // "foreach" without removing them from the stack, since they - // still can be used later to indent "else". - auto savedIndents = IndentStack(config); - while (indents.length >= 0 && indents.topIsTemp) { - savedIndents.push(indents.top, indents.topDetails); - indents.pop; - } - indentLevel = indents.indentLevel; - while (savedIndents.length > 0) { - indents.push(savedIndents.top, savedIndents.topDetails); - savedIndents.pop; - } - } - else if (astInformation.attributeDeclarationLines.canFindIndex(current.line)) - { - if (config.dfmt_outdent_attributes == OptionalBoolean.t) - { - immutable l = indents.indentToMostRecent(tok!"{"); - if (l != -1) - indentLevel = l; - } - else - { - if (indents.topIs(tok!"@")) - indents.pop(); - indentLevel = indents.indentLevel; - } - } - else if (currentIs(tok!"catch") || currentIs(tok!"finally")) - { - indentLevel = indents.indentLevel; - } - else - { - if (indents.topIsTemp() && (peekBackIsOneOf(true, tok!"}", - tok!";") && !indents.topIs(tok!";"))) - indents.popTempIndents(); - indentLevel = indents.indentLevel; - } - indent(); - } - parenDepthOnLine = 0; - } - - void write(string str) - { - currentLineLength += str.length; - output.put(str); - } - - void writeToken() - { - import std.range:retro; - import std.algorithm.searching:countUntil; - import std.algorithm.iteration:joiner; - import std.string:lineSplitter; - - if (current.text is null) - { - immutable s = str(current.type); - currentLineLength += s.length; - output.put(str(current.type)); - } - else - { - output.put(current.text.lineSplitter.joiner(eolString)); - switch (current.type) - { - case tok!"stringLiteral": - case tok!"wstringLiteral": - case tok!"dstringLiteral": - immutable o = current.text.retro().countUntil('\n'); - if (o == -1) { - currentLineLength += current.text.length; - } else { - currentLineLength = cast(uint) o; - } - break; - default: - currentLineLength += current.text.length; - break; - } - } - index++; - } - - void writeParens(bool spaceAfter) - in - { - assert(currentIs(tok!"("), str(current.type)); - } - do - { - immutable int depth = parenDepth; - immutable int startingNiBraceDepth = niBraceDepth; - immutable int startingSBraceDepth = sBraceDepth; - parenDepth = 0; - - do - { - spaceAfterParens = spaceAfter; - if (currentIs(tok!";") && niBraceDepth <= startingNiBraceDepth - && sBraceDepth <= startingSBraceDepth) - { - if (currentLineLength >= config.dfmt_soft_max_line_length) - { - pushWrapIndent(); - writeToken(); - newline(); - } - else - { - writeToken(); - if (!currentIs(tok!")") && !currentIs(tok!";")) - write(" "); - } - } - else - formatStep(); - } - while (hasCurrent && parenDepth > 0); - - if (indents.topIs(tok!"!")) - indents.pop(); - parenDepth = depth; - spaceAfterParens = spaceAfter; - } - - void indent() - { - import dfmt.editorconfig : IndentStyle; - - if (config.indent_style == IndentStyle.tab) - { - foreach (i; 0 .. indentLevel) - { - currentLineLength += config.tab_width; - output.put("\t"); - } - } - else - { - foreach (i; 0 .. indentLevel) - foreach (j; 0 .. config.indent_size) - { - output.put(" "); - currentLineLength++; - } - } - } - - void pushWrapIndent(IdType type = tok!"") - { - immutable t = type == tok!"" ? tokens[index].type : type; - IndentStack.Details detail; - detail.wrap = isWrapIndent(t); - detail.temp = isTempIndent(t); - pushWrapIndent(t, detail); - } - - void pushWrapIndent(IdType type, IndentStack.Details detail) - { - if (parenDepth == 0) - { - if (indents.wrapIndents == 0) - indents.push(type, detail); - } - else if (indents.wrapIndents < 1) - indents.push(type, detail); - } - - void writeSpace() - { - if (onNextLine) - { - newline(); - } - else - { - write(" "); - } - } - -const pure @safe @nogc: - - size_t expressionEndIndex(size_t i, bool matchComma = false) nothrow - { - immutable bool braces = i < tokens.length && tokens[i].type == tok!"{"; - immutable bool brackets = i < tokens.length && tokens[i].type == tok!"["; - immutable d = depths[i]; - while (true) - { - if (i >= tokens.length) - break; - if (depths[i] < d) - break; - if (!braces && !brackets && matchComma && depths[i] == d && tokens[i].type == tok!",") - break; - if (!braces && !brackets && (tokens[i].type == tok!";" || tokens[i].type == tok!"{")) - break; - i++; - } - return i; - } - - /// Returns: true when the expression starting at index goes over the line length limit. - /// Uses matching `{}` or `[]` or otherwise takes everything up until a semicolon or opening brace using expressionEndIndex. - bool isMultilineAt(size_t i, bool matchComma = false) - { - import std.algorithm : map, sum, canFind; - - auto e = expressionEndIndex(i, matchComma); - immutable int l = currentLineLength + tokens[i .. e].map!(a => tokenLength(a)).sum(); - return l > config.dfmt_soft_max_line_length || tokens[i .. e].canFind!( - a => a.type == tok!"comment" || isBlockHeaderToken(a.type))(); - } - - bool peekIsKeyword() nothrow - { - return index + 1 < tokens.length && isKeyword(tokens[index + 1].type); - } - - bool peekIsBasicType() nothrow - { - return index + 1 < tokens.length && isBasicType(tokens[index + 1].type); - } - - bool peekIsLabel() nothrow - { - return peekIs(tok!"identifier") && peek2Is(tok!":"); - } - - int currentTokenLength() - { - return tokenLength(tokens[index]); - } - - int nextTokenLength() - { - immutable size_t i = index + 1; - if (i >= tokens.length) - return INVALID_TOKEN_LENGTH; - return tokenLength(tokens[i]); - } - - bool hasCurrent() nothrow const - { - return index < tokens.length; - } - - ref current() nothrow - in - { - assert(hasCurrent); - } - do - { - return tokens[index]; - } - - const(Token) peekBack(uint distance = 1) nothrow - { - assert(index >= distance, "Trying to peek before the first token"); - return tokens[index - distance]; - } - - bool peekBackIsLiteralOrIdent() nothrow - { - if (index == 0) - return false; - switch (tokens[index - 1].type) - { - case tok!"doubleLiteral": - case tok!"floatLiteral": - case tok!"idoubleLiteral": - case tok!"ifloatLiteral": - case tok!"intLiteral": - case tok!"longLiteral": - case tok!"realLiteral": - case tok!"irealLiteral": - case tok!"uintLiteral": - case tok!"ulongLiteral": - case tok!"characterLiteral": - case tok!"identifier": - case tok!"stringLiteral": - case tok!"wstringLiteral": - case tok!"dstringLiteral": - case tok!"true": - case tok!"false": - return true; - default: - return false; - } - } - - bool peekIsLiteralOrIdent() nothrow - { - if (index + 1 >= tokens.length) - return false; - switch (tokens[index + 1].type) - { - case tok!"doubleLiteral": - case tok!"floatLiteral": - case tok!"idoubleLiteral": - case tok!"ifloatLiteral": - case tok!"intLiteral": - case tok!"longLiteral": - case tok!"realLiteral": - case tok!"irealLiteral": - case tok!"uintLiteral": - case tok!"ulongLiteral": - case tok!"characterLiteral": - case tok!"identifier": - case tok!"stringLiteral": - case tok!"wstringLiteral": - case tok!"dstringLiteral": - return true; - default: - return false; - } - } - - bool peekBackIs(IdType tokenType, bool ignoreComments = false) nothrow - { - return peekImplementation(tokenType, -1, ignoreComments); - } - - bool peekBackIsKeyword(bool ignoreComments = true) nothrow - { - if (index == 0) - return false; - auto i = index - 1; - if (ignoreComments) - while (tokens[i].type == tok!"comment") - { - if (i == 0) - return false; - i--; - } - return isKeyword(tokens[i].type); - } - - bool peekBackIsOperator() nothrow - { - return index == 0 ? false : isOperator(tokens[index - 1].type); - } - - bool peekBackIsOneOf(bool ignoreComments, IdType[] tokenTypes...) nothrow - { - if (index == 0) - return false; - auto i = index - 1; - if (ignoreComments) - while (tokens[i].type == tok!"comment") - { - if (i == 0) - return false; - i--; - } - immutable t = tokens[i].type; - foreach (tt; tokenTypes) - if (tt == t) - return true; - return false; - } - - bool peekBack2Is(IdType tokenType, bool ignoreComments = false) nothrow - { - return peekImplementation(tokenType, -2, ignoreComments); - } - - bool peekImplementation(IdType tokenType, int n, bool ignoreComments = true) nothrow - { - auto i = index + n; - if (ignoreComments) - while (n != 0 && i < tokens.length && tokens[i].type == tok!"comment") - i = n > 0 ? i + 1 : i - 1; - return i < tokens.length && tokens[i].type == tokenType; - } - - bool peek2Is(IdType tokenType, bool ignoreComments = true) nothrow - { - return peekImplementation(tokenType, 2, ignoreComments); - } - - bool peekIsOperator() nothrow - { - return index + 1 < tokens.length && isOperator(tokens[index + 1].type); - } - - bool peekIs(IdType tokenType, bool ignoreComments = true) nothrow - { - return peekImplementation(tokenType, 1, ignoreComments); - } - - bool peekIsBody() nothrow - { - return index + 1 < tokens.length && tokens[index + 1].text == "body"; - } - - bool peekBackIsFunctionDeclarationEnding() nothrow - { - return peekBackIsOneOf(false, tok!")", tok!"const", tok!"immutable", - tok!"inout", tok!"shared", tok!"@", tok!"pure", tok!"nothrow", - tok!"return", tok!"scope"); - } - - bool peekBackIsSlashSlash() nothrow - { - return index > 0 && tokens[index - 1].type == tok!"comment" - && tokens[index - 1].text[0 .. 2] == "//"; - } - - bool currentIs(IdType tokenType) nothrow - { - return hasCurrent && tokens[index].type == tokenType; - } - - bool onNextLine() @nogc nothrow pure @safe - { - import dfmt.editorconfig : OptionalBoolean; - import std.algorithm.searching : count; - import std.string : representation; - - if (config.dfmt_keep_line_breaks == OptionalBoolean.f || index <= 0) - { - return false; - } - // To compare whether 2 tokens are on same line, we need the end line - // of the first token (tokens[index - 1]) and the start line of the - // second one (tokens[index]). If a token takes multiple lines (e.g. a - // multi-line string), we can sum the number of the newlines in the - // token and tokens[index - 1].line, the start line. - const previousTokenEndLineNo = tokens[index - 1].line - + tokens[index - 1].text.representation.count('\n'); - - return previousTokenEndLineNo < tokens[index].line; - } - - /// Bugs: not unicode correct - size_t tokenEndLine(const Token t) - { - import std.algorithm : count; - - switch (t.type) - { - case tok!"comment": - case tok!"stringLiteral": - case tok!"wstringLiteral": - case tok!"dstringLiteral": - return t.line + t.text.count('\n'); - default: - return t.line; - } - } - - bool isBlockHeaderToken(const IdType t) - { - return t == tok!"for" || t == tok!"foreach" || t == tok!"foreach_reverse" - || t == tok!"while" || t == tok!"if" || t == tok!"in"|| t == tok!"out" - || t == tok!"do" || t == tok!"catch" || t == tok!"with" - || t == tok!"synchronized" || t == tok!"scope" || t == tok!"debug"; - } - - bool isBlockHeader(int i = 0) nothrow - { - if (i + index < 0 || i + index >= tokens.length) - return false; - const t = tokens[i + index].type; - bool isExpressionContract; - - if (i + index + 3 < tokens.length) - { - isExpressionContract = (t == tok!"in" && peekImplementation(tok!"(", i + 1, true)) - || (t == tok!"out" && (peekImplementation(tok!"(", i + 1, true) - && (peekImplementation(tok!";", i + 2, true) - || (peekImplementation(tok!"identifier", i + 2, true) - && peekImplementation(tok!";", i + 3, true))))); - } - - return isBlockHeaderToken(t) && !isExpressionContract; - } - - bool isSeparationToken(IdType t) nothrow - { - return t == tok!"," || t == tok!";" || t == tok!":" || t == tok!"(" - || t == tok!")" || t == tok!"[" || t == tok!"]" || t == tok!"{" || t == tok!"}"; - } -} - -bool canFindIndex(const size_t[] items, size_t index, size_t* pos = null) pure @safe @nogc -{ - import std.range : assumeSorted; - if (!pos) - { - return !assumeSorted(items).equalRange(index).empty; - } - else - { - auto trisection_result = assumeSorted(items).trisect(index); - if (trisection_result[1].length == 1) - { - *pos = trisection_result[0].length; - return true; - } - else if (trisection_result[1].length == 0) - { - return false; - } - else - { - assert(0, "the constraint of having unique locations has been violated"); - } - } -} diff --git a/src/dfmt/globmatch_editorconfig.d b/src/dfmt/globmatch_editorconfig.d index eff1b79..f1a2a67 100644 --- a/src/dfmt/globmatch_editorconfig.d +++ b/src/dfmt/globmatch_editorconfig.d @@ -3,7 +3,7 @@ module dfmt.globmatch_editorconfig; import std.path : CaseSensitive; import std.range : isForwardRange, ElementEncodingType; import std.traits : isSomeChar, isSomeString; -import std.range.primitives : empty, save, front, popFront; +import std.range.primitives : empty, save, front, popFront; import std.traits : Unqual; import std.conv : to; import std.path : filenameCharCmp, isDirSeparator; @@ -12,7 +12,7 @@ import std.path : filenameCharCmp, isDirSeparator; // * changes meaning to match all characters except '/' // ** added to take over the old meaning of * bool globMatchEditorConfig(CaseSensitive cs = CaseSensitive.osDefault, C, Range)( - Range path, const(C)[] pattern) @safe pure nothrow + Range path, const(C)[] pattern) @safe pure if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && isSomeChar!C && is(Unqual!C == Unqual!(ElementEncodingType!Range))) in diff --git a/src/dfmt/indentation.d b/src/dfmt/indentation.d deleted file mode 100644 index cdd78e4..0000000 --- a/src/dfmt/indentation.d +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright Brian Schott 2015. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dfmt.indentation; - -import dfmt.config; -import dfmt.editorconfig; -import dparse.lexer; - -import std.bitmanip : bitfields; - -/** - * Returns: true if the given token type is a wrap indent type - */ -bool isWrapIndent(IdType type) pure nothrow @nogc @safe -{ - return type != tok!"{" && type != tok!"case" && type != tok!"@" - && type != tok!"]" && type != tok!"(" && type != tok!")" && isOperator(type); -} - -/** - * Returns: true if the given token type is a temporary indent type - */ -bool isTempIndent(IdType type) pure nothrow @nogc @safe -{ - return type != tok!")" && type != tok!"{" && type != tok!"case" && type != tok!"@"; -} - -/** - * Stack for managing indent levels. - */ -struct IndentStack -{ - /// Configuration - private const Config* config; - - this(const Config* config) - { - this.config = config; - } - - static struct Details - { - mixin(bitfields!( - // generally true for all operators except {, case, @, ], (, ) - bool, "wrap", 1, - // temporary indentation which get's reverted when a block starts - // generally true for all tokens except ), {, case, @ - bool, "temp", 1, - // emit minimal newlines - bool, "mini", 1, - // for associative arrays or arrays containing them, break after every item - bool, "breakEveryItem", 1, - // when an item inside an array would break mid-item, definitely break at the comma first - bool, "preferLongBreaking", 1, - uint, "", 27)); - } - - /** - * Get the indent size at the most recent occurrence of the given indent type - */ - int indentToMostRecent(IdType item) const - { - if (index == 0) - return -1; - size_t i = index - 1; - while (true) - { - if (arr[i] == item) - return indentSize(i); - if (i > 0) - i--; - else - return -1; - } - } - - int wrapIndents() const pure nothrow @property - { - if (index == 0) - return 0; - int tempIndentCount = 0; - for (size_t i = index; i > 0; i--) - { - if (!details[i - 1].wrap && arr[i - 1] != tok!"]") - break; - tempIndentCount++; - } - return tempIndentCount; - } - - /** - * Pushes the given indent type on to the stack. - */ - void push(IdType item) pure nothrow - { - Details detail; - detail.wrap = isWrapIndent(item); - detail.temp = isTempIndent(item); - push(item, detail); - } - - /** - * Pushes the given indent type on to the stack. - */ - void push(IdType item, Details detail) pure nothrow - { - arr[index] = item; - details[index] = detail; - //FIXME this is actually a bad thing to do, - //we should not just override when the stack is - //at it's limit - if (index < arr.length) - { - index++; - } - } - - /** - * Pops the top indent from the stack. - */ - void pop() pure nothrow - { - if (index) - index--; - } - - /** - * Pops all wrapping indents from the top of the stack. - */ - void popWrapIndents() pure nothrow @safe @nogc - { - while (index > 0 && details[index - 1].wrap) - index--; - } - - /** - * Pops all temporary indents from the top of the stack. - */ - void popTempIndents() pure nothrow @safe @nogc - { - while (index > 0 && details[index - 1].temp) - index--; - } - - bool topAre(IdType[] types...) - { - if (types.length > index) - return false; - return arr[index - types.length .. index] == types; - - } - - /** - * Returns: `true` if the top of the indent stack is the given indent type. - */ - bool topIs(IdType type) const pure nothrow @safe @nogc - { - return index > 0 && index <= arr.length && arr[index - 1] == type; - } - - /** - * Returns: `true` if the top of the indent stack is a temporary indent - */ - bool topIsTemp() - { - return index > 0 && index <= arr.length && details[index - 1].temp; - } - - /** - * Returns: `true` if the top of the indent stack is a temporary indent with the specified token - */ - bool topIsTemp(IdType item) - { - return index > 0 && index <= arr.length && arr[index - 1] == item && details[index - 1].temp; - } - - /** - * Returns: `true` if the top of the indent stack is a wrapping indent - */ - bool topIsWrap() - { - return index > 0 && index <= arr.length && details[index - 1].wrap; - } - - /** - * Returns: `true` if the top of the indent stack is a temporary indent with the specified token - */ - bool topIsWrap(IdType item) - { - return index > 0 && index <= arr.length && arr[index - 1] == item && details[index - 1].wrap; - } - - /** - * Returns: `true` if the top of the indent stack is one of the given token - * types. - */ - bool topIsOneOf(IdType[] types...) const pure nothrow @safe @nogc - { - if (index == 0) - return false; - immutable topType = arr[index - 1]; - foreach (t; types) - if (t == topType) - return true; - return false; - } - - IdType top() const pure nothrow @property @safe @nogc - { - return arr[index - 1]; - } - - Details topDetails() const pure nothrow @property @safe @nogc - { - return details[index - 1]; - } - - int indentLevel() const pure nothrow @property @safe @nogc - { - return indentSize(); - } - - int length() const pure nothrow @property @safe @nogc - { - return cast(int) index; - } - - /** - * Dumps the current state of the indentation stack to `stderr`. Used for debugging. - */ - void dump(size_t pos = size_t.max, string file = __FILE__, uint line = __LINE__) const - { - import dparse.lexer : str; - import std.algorithm.iteration : map; - import std.stdio : stderr; - - if (pos == size_t.max) - stderr.writefln("\033[31m%s:%d %(%s %)\033[0m", file, line, arr[0 .. index].map!(a => str(a))); - else - stderr.writefln("\033[31m%s:%d at %d %(%s %)\033[0m", file, line, pos, arr[0 .. index].map!(a => str(a))); - } - -private: - - size_t index; - - IdType[256] arr; - Details[arr.length] details; - - int indentSize(const size_t k = size_t.max) const pure nothrow @safe @nogc - { - import std.algorithm : among; - if (index == 0 || k == 0) - return 0; - immutable size_t j = k == size_t.max ? index : k; - int size = 0; - int parenCount; - foreach (i; 0 .. j) - { - immutable int pc = (arr[i] == tok!"!" || arr[i] == tok!"(" || arr[i] == tok!")") ? parenCount + 1 - : parenCount; - if ((details[i].wrap || arr[i] == tok!"(") && parenCount > 1) - { - parenCount = pc; - continue; - } - - if (i + 1 < index) - { - if (config.dfmt_single_indent == OptionalBoolean.t && skipDoubleIndent(i, parenCount)) - { - parenCount = pc; - continue; - } - - immutable currentIsNonWrapTemp = !details[i].wrap - && details[i].temp && arr[i] != tok!")" && arr[i] != tok!"!"; - - if (currentIsNonWrapTemp && arr[i + 1] == tok!"]") - { - parenCount = pc; - continue; - } - if (arr[i] == tok!"static" - && arr[i + 1].among!(tok!"if", tok!"else", tok!"foreach", tok!"foreach_reverse") - && (i + 2 >= index || arr[i + 2] != tok!"{")) - { - parenCount = pc; - continue; - } - if (currentIsNonWrapTemp && (arr[i + 1] == tok!"switch" - || arr[i + 1] == tok!"{" || arr[i + 1] == tok!")")) - { - parenCount = pc; - continue; - } - } - else if (parenCount == 0 && arr[i] == tok!"(" && config.dfmt_single_indent == OptionalBoolean.f) - size++; - - if (arr[i] == tok!"!") - size++; - - parenCount = pc; - size++; - } - return size; - } - - bool skipDoubleIndent(size_t i, int parenCount) const pure nothrow @safe @nogc - { - return (details[i + 1].wrap && arr[i] == tok!")") - || (parenCount == 0 && arr[i + 1] == tok!"," && arr[i] == tok!"("); - } -} - -unittest -{ - IndentStack stack; - stack.push(tok!"{"); - assert(stack.length == 1); - assert(stack.indentLevel == 1); - stack.pop(); - assert(stack.length == 0); - assert(stack.indentLevel == 0); - stack.push(tok!"if"); - assert(stack.topIsTemp()); - stack.popTempIndents(); - assert(stack.length == 0); -} diff --git a/src/dfmt/main.d b/src/dfmt/main.d index c5a5577..8a99175 100644 --- a/src/dfmt/main.d +++ b/src/dfmt/main.d @@ -30,91 +30,84 @@ static immutable VERSION = () { } return DFMT_VERSION ~ DEBUG_SUFFIX; -} (); +}(); +import dfmt.config : Config; +import dfmt.editorconfig : getConfigFor; +import dfmt.formatter : format; +import std.array : appender, front, popFront; +import std.getopt : getopt, GetOptException; +import std.path : buildPath, dirName, expandTilde; +import std.stdio : File, stderr, stdin, stdout, writeln; -version (NoMain) +int main(string[] args) { -} -else -{ - import dfmt.config : Config; - import dfmt.editorconfig : getConfigFor; - import dfmt.formatter : format; - import std.array : appender, front, popFront; - import std.getopt : getopt, GetOptException; - import std.path : buildPath, dirName, expandTilde; - import std.stdio : File, stderr, stdin, stdout, writeln; + bool inplace = false; + Config optConfig; + optConfig.pattern = "*.d"; + bool showHelp; + bool showVersion; + string explicitConfigDir; - int main(string[] args) + void handleBooleans(string option, string value) { - bool inplace = false; - Config optConfig; - optConfig.pattern = "*.d"; - bool showHelp; - bool showVersion; - string explicitConfigDir; + import dfmt.editorconfig : OptionalBoolean; + import std.exception : enforce; - void handleBooleans(string option, string value) + enforce!GetOptException(value == "true" || value == "false", "Invalid argument"); + immutable OptionalBoolean optVal = value == "true" ? OptionalBoolean.t : OptionalBoolean.f; + switch (option) { - import dfmt.editorconfig : OptionalBoolean; - import std.exception : enforce; - - enforce!GetOptException(value == "true" || value == "false", "Invalid argument"); - immutable OptionalBoolean optVal = value == "true" ? OptionalBoolean.t - : OptionalBoolean.f; - switch (option) - { - case "align_switch_statements": - optConfig.dfmt_align_switch_statements = optVal; - break; - case "outdent_attributes": - optConfig.dfmt_outdent_attributes = optVal; - break; - case "space_after_cast": - optConfig.dfmt_space_after_cast = optVal; - break; - case "space_before_function_parameters": - optConfig.dfmt_space_before_function_parameters = optVal; - break; - case "split_operator_at_line_end": - optConfig.dfmt_split_operator_at_line_end = optVal; - break; - case "selective_import_space": - optConfig.dfmt_selective_import_space = optVal; - break; - case "compact_labeled_statements": - optConfig.dfmt_compact_labeled_statements = optVal; - break; - case "single_template_constraint_indent": - optConfig.dfmt_single_template_constraint_indent = optVal; - break; - case "space_before_aa_colon": - optConfig.dfmt_space_before_aa_colon = optVal; - break; - case "space_before_named_arg_colon": - optConfig.dfmt_space_before_named_arg_colon = optVal; - break; - case "keep_line_breaks": - optConfig.dfmt_keep_line_breaks = optVal; - break; - case "single_indent": - optConfig.dfmt_single_indent = optVal; - break; - case "reflow_property_chains": - optConfig.dfmt_reflow_property_chains = optVal; - break; - case "space_after_keywords": - optConfig.dfmt_space_after_keywords = optVal; - break; - default: - assert(false, "Invalid command-line switch"); - } + case "align_switch_statements": + optConfig.dfmt_align_switch_statements = optVal; + break; + case "outdent_attributes": + optConfig.dfmt_outdent_attributes = optVal; + break; + case "space_after_cast": + optConfig.dfmt_space_after_cast = optVal; + break; + case "space_before_function_parameters": + optConfig.dfmt_space_before_function_parameters = optVal; + break; + case "split_operator_at_line_end": + optConfig.dfmt_split_operator_at_line_end = optVal; + break; + case "selective_import_space": + optConfig.dfmt_selective_import_space = optVal; + break; + case "compact_labeled_statements": + optConfig.dfmt_compact_labeled_statements = optVal; + break; + case "single_template_constraint_indent": + optConfig.dfmt_single_template_constraint_indent = optVal; + break; + case "space_before_aa_colon": + optConfig.dfmt_space_before_aa_colon = optVal; + break; + case "space_before_named_arg_colon": + optConfig.dfmt_space_before_named_arg_colon = optVal; + break; + case "keep_line_breaks": + optConfig.dfmt_keep_line_breaks = optVal; + break; + case "single_indent": + optConfig.dfmt_single_indent = optVal; + break; + case "reflow_property_chains": + optConfig.dfmt_reflow_property_chains = optVal; + break; + case "space_after_keywords": + optConfig.dfmt_space_after_keywords = optVal; + break; + default: + assert(false, "Invalid command-line switch"); } + } - try - { - // dfmt off + try + { + // dfmt off getopt(args, "version", &showVersion, "align_switch_statements", &handleBooleans, @@ -143,68 +136,116 @@ else "single_indent", &handleBooleans, "reflow_property_chains", &handleBooleans); // dfmt on - } - catch (GetOptException e) + } + catch (GetOptException e) + { + stderr.writeln(e.msg); + return 1; + } + + if (showVersion) + { + writeln(VERSION); + return 0; + } + + if (showHelp) + { + printHelp(); + return 0; + } + + args.popFront(); + immutable bool readFromStdin = args.length == 0; + + version (Windows) + { + // On Windows, set stdout to binary mode (needed for correct EOL writing) + // See Phobos' stdio.File.rawWrite { - stderr.writeln(e.msg); + import std.stdio : _O_BINARY; + + immutable fd = stdout.fileno; + _setmode(fd, _O_BINARY); + version (CRuntime_DigitalMars) + { + import core.atomic : atomicOp; + import core.stdc.stdio : __fhnd_info, FHND_TEXT; + + atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); + } + } + } + + ubyte[] buffer; + + Config explicitConfig; + if (explicitConfigDir) + { + import std.file : exists, isDir; + + if (!exists(explicitConfigDir) || !isDir(explicitConfigDir)) + { + stderr.writeln("--config|c must specify existing directory path"); return 1; } + explicitConfig = getConfigFor!Config(explicitConfigDir); + explicitConfig.pattern = "*.d"; + } - if (showVersion) + if (readFromStdin) + { + import std.file : getcwd; + + auto cwdDummyPath = buildPath(getcwd(), "dummy.d"); + + Config config; + config.initializeWithDefaults(); + if (explicitConfigDir != "") { - writeln(VERSION); - return 0; + config.merge(explicitConfig, buildPath(explicitConfigDir, "dummy.d")); } - - if (showHelp) + else { - printHelp(); - return 0; + Config fileConfig = getConfigFor!Config(getcwd()); + fileConfig.pattern = "*.d"; + config.merge(fileConfig, cwdDummyPath); } - - args.popFront(); - immutable bool readFromStdin = args.length == 0; - - version (Windows) + config.merge(optConfig, cwdDummyPath); + if (!config.isValid()) + return 1; + ubyte[4096] inputBuffer; + ubyte[] b; + while (true) { - // On Windows, set stdout to binary mode (needed for correct EOL writing) - // See Phobos' stdio.File.rawWrite + b = stdin.rawRead(inputBuffer); + if (b.length) + buffer ~= b; + else + break; + } + immutable bool formatSuccess = format("stdin", buffer, + stdout.lockingTextWriter(), &config); + return formatSuccess ? 0 : 1; + } + else + { + import std.file : dirEntries, isDir, SpanMode; + + if (args.length >= 2) + inplace = true; + int retVal; + while (args.length > 0) + { + const path = args.front; + args.popFront(); + if (isDir(path)) { - import std.stdio : _O_BINARY; - immutable fd = stdout.fileno; - _setmode(fd, _O_BINARY); - version (CRuntime_DigitalMars) - { - import core.atomic : atomicOp; - import core.stdc.stdio : __fhnd_info, FHND_TEXT; - - atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); - } + inplace = true; + foreach (string name; dirEntries(path, "*.d", SpanMode.depth)) + args ~= name; + continue; } - } - - ubyte[] buffer; - - Config explicitConfig; - if (explicitConfigDir) - { - import std.file : exists, isDir; - - if (!exists(explicitConfigDir) || !isDir(explicitConfigDir)) - { - stderr.writeln("--config|c must specify existing directory path"); - return 1; - } - explicitConfig = getConfigFor!Config(explicitConfigDir); - explicitConfig.pattern = "*.d"; - } - - if (readFromStdin) - { - import std.file : getcwd; - - auto cwdDummyPath = buildPath(getcwd(), "dummy.d"); - Config config; config.initializeWithDefaults(); if (explicitConfigDir != "") @@ -213,94 +254,42 @@ else } else { - Config fileConfig = getConfigFor!Config(getcwd()); + Config fileConfig = getConfigFor!Config(path); fileConfig.pattern = "*.d"; - config.merge(fileConfig, cwdDummyPath); + config.merge(fileConfig, path); } - config.merge(optConfig, cwdDummyPath); + config.merge(optConfig, path); if (!config.isValid()) return 1; - ubyte[4096] inputBuffer; - ubyte[] b; - while (true) + File f = File(path); + // ignore empty files + if (f.size) { - b = stdin.rawRead(inputBuffer); - if (b.length) - buffer ~= b; + buffer = new ubyte[](cast(size_t) f.size); + f.rawRead(buffer); + File.LockingTextWriter output; + if (inplace) + output = File(path, "wb").lockingTextWriter(); else - break; + output = stdout.lockingTextWriter(); + immutable bool formatSuccess = format(path, buffer, output, &config); + retVal = formatSuccess ? 0 : 1; } - immutable bool formatSuccess = format("stdin", buffer, - stdout.lockingTextWriter(), &config); - return formatSuccess ? 0 : 1; - } - else - { - import std.file : dirEntries, isDir, SpanMode; - - if (args.length >= 2) - inplace = true; - int retVal; - while (args.length > 0) - { - const path = args.front; - args.popFront(); - if (isDir(path)) - { - inplace = true; - foreach (string name; dirEntries(path, "*.d", SpanMode.depth)) - args ~= name; - continue; - } - Config config; - config.initializeWithDefaults(); - if (explicitConfigDir != "") - { - config.merge(explicitConfig, buildPath(explicitConfigDir, "dummy.d")); - } - else - { - Config fileConfig = getConfigFor!Config(path); - fileConfig.pattern = "*.d"; - config.merge(fileConfig, path); - } - config.merge(optConfig, path); - if (!config.isValid()) - return 1; - File f = File(path); - // ignore empty files - if (f.size) - { - buffer = new ubyte[](cast(size_t) f.size); - f.rawRead(buffer); - auto output = appender!string; - immutable bool formatSuccess = format(path, buffer, output, &config); - if (formatSuccess) - { - if (inplace) - File(path, "wb").rawWrite(output.data); - else - stdout.rawWrite(output.data); - } - else - retVal = 1; - } - } - return retVal; } + return retVal; } } private version (Windows) { - version(CRuntime_DigitalMars) + version (CRuntime_DigitalMars) { - extern(C) int setmode(int, int) nothrow @nogc; + extern (C) int setmode(int, int) nothrow @nogc; alias _setmode = setmode; } - else version(CRuntime_Microsoft) + else version (CRuntime_Microsoft) { - extern(C) int _setmode(int, int) nothrow @nogc; + extern (C) int _setmode(int, int) nothrow @nogc; } } @@ -318,11 +307,14 @@ template optionsToString(E) if (is(E == enum)) } result = result[0 .. $ - 1] ~ ")"; return result; - } (); + }(); } private void printHelp() { + import std.stdio : writeln; + import dfmt.config; + writeln(`dfmt `, VERSION, ` https://github.com/dlang-community/dfmt @@ -334,12 +326,10 @@ Options: Formatting Options: --align_switch_statements - --brace_style `, optionsToString!(typeof(Config.dfmt_brace_style)), - ` + --brace_style `, optionsToString!(typeof(Config.dfmt_brace_style)), ` --end_of_line `, optionsToString!(typeof(Config.end_of_line)), ` --indent_size - --indent_style, -t `, - optionsToString!(typeof(Config.indent_style)), ` + --indent_style, -t `, optionsToString!(typeof(Config.indent_style)), ` --keep_line_breaks --soft_max_line_length --max_line_length @@ -356,14 +346,13 @@ Formatting Options: --space_before_named_arg_colon --single_indent --reflow_property_chains - `, - optionsToString!(typeof(Config.dfmt_template_constraint_style))); + `, optionsToString!(typeof(Config.dfmt_template_constraint_style))); } private string createFilePath(bool readFromStdin, string fileName) { import std.file : getcwd; - import std.path : isRooted; + import std.path : isRooted, buildPath; immutable string cwd = getcwd(); if (readFromStdin) diff --git a/src/dfmt/tokens.d b/src/dfmt/tokens.d deleted file mode 100644 index 0271fde..0000000 --- a/src/dfmt/tokens.d +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright Brian Schott 2015. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dfmt.tokens; - -import dparse.lexer; - -/// Length of an invalid token -enum int INVALID_TOKEN_LENGTH = -1; - -uint betweenParenLength(const Token[] tokens) pure @safe @nogc -in -{ - assert(tokens[0].type == tok!"("); -} -do -{ - uint length = 0; - size_t i = 1; - int depth = 1; - while (i < tokens.length && depth > 0) - { - if (tokens[i].type == tok!"(") - depth++; - else if (tokens[i].type == tok!")") - depth--; - length += tokenLength(tokens[i]); - i++; - } - return length; -} - -int tokenLength(ref const Token t) pure @safe @nogc -{ - import std.algorithm : countUntil; - - int c; - switch (t.type) - { - case tok!"doubleLiteral": - case tok!"floatLiteral": - case tok!"idoubleLiteral": - case tok!"ifloatLiteral": - case tok!"intLiteral": - case tok!"longLiteral": - case tok!"realLiteral": - case tok!"irealLiteral": - case tok!"uintLiteral": - case tok!"ulongLiteral": - case tok!"characterLiteral": - return cast(int) t.text.length; - case tok!"identifier": - case tok!"stringLiteral": - case tok!"wstringLiteral": - case tok!"dstringLiteral": - // TODO: Unicode line breaks and old-Mac line endings - c = cast(int) t.text.countUntil('\n'); - if (c == -1) - return cast(int) t.text.length; - else - return c; - mixin(generateFixedLengthCases()); - default: - return INVALID_TOKEN_LENGTH; - } -} - -bool isBreakToken(IdType t) pure nothrow @safe @nogc -{ - switch (t) - { - case tok!"||": - case tok!"&&": - case tok!"(": - case tok!"[": - case tok!",": - case tok!":": - case tok!";": - case tok!"^^": - case tok!"^=": - case tok!"^": - case tok!"~=": - case tok!"<<=": - case tok!"<<": - case tok!"<=": - case tok!"<>=": - case tok!"<>": - case tok!"<": - case tok!"==": - case tok!"=>": - case tok!"=": - case tok!">=": - case tok!">>=": - case tok!">>>=": - case tok!">>>": - case tok!">>": - case tok!">": - case tok!"|=": - case tok!"|": - case tok!"-=": - case tok!"!<=": - case tok!"!<>=": - case tok!"!<>": - case tok!"!<": - case tok!"!=": - case tok!"!>=": - case tok!"!>": - case tok!"?": - case tok!"/=": - case tok!"/": - case tok!"..": - case tok!"*=": - case tok!"*": - case tok!"&=": - case tok!"%=": - case tok!"%": - case tok!"+=": - case tok!".": - case tok!"~": - case tok!"+": - case tok!"-": - return true; - default: - return false; - } -} - -int breakCost(IdType p, IdType c) pure nothrow @safe @nogc -{ - switch (c) - { - case tok!"||": - case tok!"&&": - case tok!",": - case tok!"?": - return 0; - case tok!"(": - return 60; - case tok!"[": - return 300; - case tok!";": - case tok!"^^": - case tok!"^=": - case tok!"^": - case tok!"~=": - case tok!"<<=": - case tok!"<<": - case tok!"<=": - case tok!"<>=": - case tok!"<>": - case tok!"<": - case tok!"==": - case tok!"=>": - case tok!"=": - case tok!">=": - case tok!">>=": - case tok!">>>=": - case tok!">>>": - case tok!">>": - case tok!">": - case tok!"|=": - case tok!"|": - case tok!"-=": - case tok!"!<=": - case tok!"!<>=": - case tok!"!<>": - case tok!"!<": - case tok!"!=": - case tok!"!>=": - case tok!"!>": - case tok!"/=": - case tok!"/": - case tok!"..": - case tok!"*=": - case tok!"*": - case tok!"&=": - case tok!"%=": - case tok!"%": - case tok!"+": - case tok!"-": - case tok!"~": - case tok!"+=": - return 200; - case tok!":": - // colon could be after a label or an import, where it should normally wrap like before - // for everything else (associative arrays) try not breaking around colons - return p == tok!"identifier" ? 0 : 300; - case tok!".": - return p == tok!")" ? 0 : 300; - default: - return 1000; - } -} - -pure nothrow @safe @nogc unittest -{ - foreach (ubyte u; 0 .. ubyte.max) - if (isBreakToken(u)) - assert(breakCost(tok!".", u) != 1000); -} - -private string generateFixedLengthCases() -{ - import std.algorithm : map; - import std.string : format; - import std.array : join; - - assert(__ctfe); - - string[] spacedOperatorTokens = [ - ",", "..", "...", "/", "/=", "!", "!<", "!<=", "!<>", "!<>=", "!=", - "!>", "!>=", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", "-", - "-=", ":", ";", "<", "<<", "<<=", "<=", "<>", "<>=", "=", "==", "=>", - ">", ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "^", "^=", "^^", - "^^=", "|", "|=", "||", "~", "~=" - ]; - immutable spacedOperatorTokenCases = spacedOperatorTokens.map!( - a => format(`case tok!"%s": return %d + 1;`, a, a.length)).join("\n\t"); - - string[] identifierTokens = [ - "abstract", "alias", "align", "asm", "assert", "auto", "bool", - "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", "char", "class", - "const", "continue", "creal", "dchar", "debug", "default", "delegate", "delete", "deprecated", - "do", "double", "else", "enum", "export", "extern", "false", "final", "finally", "float", - "for", "foreach", "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable", - "import", "in", "inout", "int", "interface", "invariant", "ireal", "is", - "lazy", "long", "macro", "mixin", "module", "new", "nothrow", "null", "out", "override", - "package", "pragma", "private", "protected", "public", "pure", "real", "ref", "return", "scope", - "shared", "short", "static", "struct", "super", "switch", "synchronized", "template", "this", - "throw", "true", "try", "typedef", "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", - "union", "unittest", "ushort", "version", "void", "wchar", - "while", "with", "__DATE__", "__EOF__", "__FILE__", - "__FUNCTION__", "__gshared", "__LINE__", "__MODULE__", "__parameters", - "__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__", - "__traits", "__vector", "__VENDOR__", "__VERSION__", "$", "++", "--", - ".", "[", "]", "(", ")", "{", "}" - ]; - immutable identifierTokenCases = identifierTokens.map!( - a => format(`case tok!"%s": return %d;`, a, a.length)).join("\n\t"); - return spacedOperatorTokenCases ~ identifierTokenCases; -} diff --git a/src/dfmt/wrapping.d b/src/dfmt/wrapping.d deleted file mode 100644 index 79fb85f..0000000 --- a/src/dfmt/wrapping.d +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright Brian Schott 2015. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -module dfmt.wrapping; - -import dparse.lexer; -import dfmt.tokens; -import dfmt.config; - -struct State -{ - this(uint breaks, const Token[] tokens, immutable short[] depths, - const Config* config, int currentLineLength, int indentLevel) pure @safe - { - import std.math : abs; - import core.bitop : popcnt, bsf; - import std.algorithm : min, map, sum; - - immutable int remainingCharsMultiplier = 25; - immutable int newlinePenalty = 480; - - this.breaks = breaks; - this._cost = 0; - this._solved = true; - - if (breaks == 0) - { - immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum(); - if (l > config.dfmt_soft_max_line_length) - { - immutable int longPenalty = (l - config.dfmt_soft_max_line_length) - * remainingCharsMultiplier; - this._cost += longPenalty; - this._solved = longPenalty < newlinePenalty; - } - } - else - { - foreach (size_t i; 0 .. (uint.sizeof * 8)) - { - if (((1 << i) & breaks) == 0) - continue; - immutable prevType = i > 0 ? tokens[i - 1].type : tok!""; - immutable currentType = tokens[i].type; - immutable p = abs(depths[i]); - immutable bc = breakCost(prevType, currentType) * (p == 0 ? 1 : p * 2); - this._cost += bc + newlinePenalty; - } - - int ll = currentLineLength; - size_t i = 0; - foreach (_; 0 .. uint.sizeof * 8) - { - immutable uint k = breaks >>> i; - immutable bool b = k == 0; - immutable uint bits = b ? ALGORITHMIC_COMPLEXITY_SUCKS : bsf(k); - immutable size_t j = min(i + bits + 1, tokens.length); - ll += tokens[i .. j].map!(a => tokenLength(a)).sum(); - if (ll > config.dfmt_soft_max_line_length) - { - immutable int longPenalty = (ll - config.dfmt_soft_max_line_length) - * remainingCharsMultiplier; - this._cost += longPenalty; - } - if (ll > config.max_line_length) - { - this._solved = false; - break; - } - i = j; - if (indentLevel < 0) - ll = (abs(indentLevel) + 1) * config.indent_size; - else - ll = (indentLevel + (i == 0 ? 0 : 1)) * config.indent_size; - if (b) - break; - } - } - } - - int cost() const pure nothrow @safe @property - { - return _cost; - } - - int solved() const pure nothrow @safe @property - { - return _solved; - } - - int opCmp(ref const State other) const pure nothrow @safe - { - import core.bitop : bsf, popcnt; - - if (_cost < other._cost) - return -1; - if (_cost == other._cost && (breaks != 0 && other.breaks != 0 - && bsf(breaks) > bsf(other.breaks))) - return -1; - return _cost > other._cost; - } - - bool opEquals(ref const State other) const pure nothrow @safe - { - return other.breaks == breaks; - } - - size_t toHash() const pure nothrow @safe - { - return breaks; - } - - uint breaks; - -private: - int _cost; - bool _solved; -} - -private enum ALGORITHMIC_COMPLEXITY_SUCKS = uint.sizeof * 8; - -/** - * Note: Negative values for `indentLevel` are treated specially: costs for - * continuation indents are reduced. This is used for array literals. - */ -size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, - immutable short[] depths, const Config* config, int currentLineLength, int indentLevel) -{ - import std.container.rbtree : RedBlackTree; - import std.algorithm : filter, min; - import core.bitop : popcnt; - - static size_t[] genRetVal(uint breaks, size_t index) pure nothrow @safe - { - auto retVal = new size_t[](popcnt(breaks)); - size_t j = 0; - foreach (uint i; 0 .. uint.sizeof * 8) - if ((1 << i) & breaks) - retVal[j++] = index + i; - return retVal; - } - - immutable size_t tokensEnd = min(tokens.length, ALGORITHMIC_COMPLEXITY_SUCKS); - auto open = new RedBlackTree!State; - open.insert(State(0, tokens[0 .. tokensEnd], depths[0 .. tokensEnd], config, - currentLineLength, indentLevel)); - State lowest; - lowest._solved = false; - int tries = 0; - while (!open.empty && tries < 10_00) - { - tries++; - State current = open.front(); - open.removeFront(); - if (current.solved) - return genRetVal(current.breaks, index); - if (current < lowest) - lowest = current; - validMoves!(typeof(open))(open, tokens[0 .. tokensEnd], depths[0 .. tokensEnd], - current.breaks, config, currentLineLength, indentLevel); - } - foreach (r; open[].filter!(a => a.solved)) - return genRetVal(r.breaks, index); - if (open[].front < lowest) - return genRetVal(open[].front.breaks, index); - else - return genRetVal(lowest.breaks, index); -} - -void validMoves(OR)(auto ref OR output, const Token[] tokens, immutable short[] depths, - uint current, const Config* config, int currentLineLength, int indentLevel) -{ - foreach (i, token; tokens) - { - if (!isBreakToken(token.type) || (((1 << i) & current) != 0)) - continue; - immutable uint breaks = current | (1 << i); - output.insert(State(breaks, tokens, depths, config, currentLineLength, indentLevel)); - } -}