diff --git a/.gitignore b/.gitignore index cebcfca..0dda609 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin *.d.out .dub dfmt +dub.selections.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..43d013f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,2 @@ +language: d +script: make test diff --git a/README.md b/README.md index 7fb899c..784eb87 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ # dfmt -Dfmt is a formatter for D source code +**dfmt** is a formatter for D source code + +[![Build Status](https://travis-ci.org/Hackerpilot/dfmt.svg)](https://travis-ci.org/Hackerpilot/dfmt) + +## Status +**dfmt** is alpha-quality. Make backups of your files or use source control. + + +## Building +### Using Make +* Clone the repository +* Run ```git submodule update --init``` in the dfmt directory +* To compile with DMD, run ```make``` in the dfmt directory. To compile with + LDC, run ```make ldc``` instead. The generated binary will be placed in ```dfmt/bin/```. + + +## Using +By default, dfmt reads its input from ```stdin``` and writes to ```stdout```. +If a file name is specified on the command line, input will be read from the +file instead, and output will be written to ```stdout```. If the ```--inplace``` +option is specified a file name is required and the file will be edited +in-place. diff --git a/libdparse b/libdparse index bce4717..f73bed9 160000 --- a/libdparse +++ b/libdparse @@ -1 +1 @@ -Subproject commit bce47174cc2ea6cfd26d007a9c41108e4fb28f0e +Subproject commit f73bed9279602a228933b21156d8f1f2ec5e4fdd diff --git a/src/dfmt.d b/src/dfmt.d index 214797b..6e95e18 100644 --- a/src/dfmt.d +++ b/src/dfmt.d @@ -34,37 +34,24 @@ import std.d.formatter; import std.d.ast; import std.array; -/// Help text -immutable USAGE = "usage: %s [--inplace] [...] -Formats D code. - - --inplace change file in-place instead of outputing to stdout - (implicit in case of multiple files) - -h, --help display this help and exit -"; - +version (NoMain) +{ } +else int main(string[] args) { import std.getopt : getopt; - import std.path: baseName; - import std.file : isDir, dirEntries, SpanMode; bool inplace = false; bool show_usage = false; - - try - { - getopt(args, - "help|h", &show_usage, - "inplace", &inplace); - } - catch (Exception e) - { - writef(USAGE, baseName(args[0])); - return 1; - } + FormatterConfig formatterConfig; + getopt(args, + "help|h", &show_usage, + "inplace", &inplace, + "tabs|t", &formatterConfig.useTabs, + "braces", &formatterConfig.braceStyle); if (show_usage) { + import std.path: baseName; writef(USAGE, baseName(args[0])); return 0; } @@ -83,10 +70,11 @@ int main(string[] args) else break; } - format("stdin", buffer, output); + format("stdin", buffer, output.lockingTextWriter(), &formatterConfig); } else { + import std.file : dirEntries, isDir, SpanMode; if (args.length >= 2) inplace = true; while (args.length > 0) @@ -107,19 +95,27 @@ int main(string[] args) f.rawRead(buffer); if (inplace) output = File(path, "w"); - format(path, buffer, output); + format(path, buffer, output.lockingTextWriter(), &formatterConfig); } } return 0; } -/** - * Params: - * source_desc = - * buffer = - * output = - */ -void format(string source_desc, ubyte[] buffer, File output) +private: + +immutable USAGE = "usage: %s [--inplace] [...] +Formats D code. + + --inplace Change file in-place instead of outputing to stdout + (implicit in case of multiple files) + --tabs | -t Use tabs instead of spaces for indentation + --braces=allman Use Allman indent style (default) + --braces=otbs Use the One True Brace Style + --help | -h Display this help and exit +"; + +void format(OutputRange)(string source_desc, ubyte[] buffer, OutputRange output, + FormatterConfig* formatterConfig) { LexerConfig config; config.stringBehavior = StringBehavior.source; @@ -129,29 +125,27 @@ void format(string source_desc, ubyte[] buffer, File output) parseConfig.whitespaceBehavior = WhitespaceBehavior.skip; StringCache cache = StringCache(StringCache.defaultBucketCount); ASTInformation astInformation; - FormatterConfig formatterConfig; auto parseTokens = getTokensForParser(buffer, parseConfig, &cache); auto mod = parseModule(parseTokens, source_desc); auto visitor = new FormatVisitor(&astInformation); visitor.visit(mod); astInformation.cleanup(); auto tokens = byToken(buffer, config, &cache).array(); - auto tokenFormatter = TokenFormatter(tokens, output, &astInformation, - &formatterConfig); + auto tokenFormatter = TokenFormatter!OutputRange(tokens, output, &astInformation, + formatterConfig); tokenFormatter.format(); } -/// Contains formatting logic -struct TokenFormatter +struct TokenFormatter(OutputRange) { /** * Params: * tokens = the tokens to format - * output = the file that the code will be formatted to + * output = the output range that the code will be formatted to * astInformation = information about the AST used to inform formatting * decisions. */ - this(const(Token)[] tokens, File output, ASTInformation* astInformation, + this(const(Token)[] tokens, OutputRange output, ASTInformation* astInformation, FormatterConfig* config) { this.tokens = tokens; @@ -184,7 +178,7 @@ private: const i = index; if (i > 0) { - if (tokens[i-1].line < tokens[i].line) + if (tokens[i-1].line < current.line) { if (tokens[i-1].type != tok!"comment" && tokens[i-1].type != tok!"{") @@ -194,11 +188,16 @@ private: write(" "); } writeToken(); - if (i + 1 >= tokens.length) + if (i >= tokens.length-1) newline(); - else if (tokens[i + 1].line > tokens[i].line) + else if (tokens[i+1].line-1 > tokens[i].line) + { newline(); - else if (tokens[i + 1].type != tok!"{") + newline(); + } + else if (tokens[i+1].line > tokens[i].line) + newline(); + else if (tokens[i+1].type != tok!"{") write(" "); } else if (isStringLiteral(current.type) || isNumberLiteral(current.type) @@ -217,11 +216,40 @@ private: { writeToken(); tempIndent = 0; + if (index >= tokens.length) + { + newline(); + break; + } + if (current.type == tok!"comment") + break; if (!(t == tok!"import" && current.type == tok!"import")) write("\n"); newline(); break; } + else if (current.type == tok!",") + { + // compute length until next , or ; + int length_of_next_chunk = INVALID_TOKEN_LENGTH; + for (size_t i=index+1; i= 0); + length_of_next_chunk += len; + } + assert (length_of_next_chunk > 0); + writeToken(); + if (currentLineLength + 1 + length_of_next_chunk >= config.columnSoftLimit) + { + pushIndent(); + newline(); + } + else + write(" "); + } else formatStep(); } @@ -229,10 +257,17 @@ private: else if (current.type == tok!"return") { writeToken(); - write(" "); + if (current.type != tok!";") + write(" "); } else if (current.type == tok!"switch") formatSwitch(); + else if (current.type == tok!"version" && peekIs(tok!"(")) + { + writeToken(); + write(" "); + writeParens(false); + } else if (current.type == tok!"for" || current.type == tok!"foreach" || current.type == tok!"foreach_reverse" || current.type == tok!"while" || current.type == tok!"if") @@ -240,7 +275,7 @@ private: currentLineLength += currentTokenLength() + 1; writeToken(); write(" "); - writeParens(); + writeParens(false); if (current.type != tok!"{" && current.type != tok!";") { pushIndent(); @@ -311,18 +346,7 @@ private: } goto binary; case tok!"(": - writeParens(); - break; - case tok!":": - if (!assumeSorted(astInformation.ternaryColonLocations) - .equalRange(current.index).empty) - { - write(" "); - writeToken(); - write(" "); - } - else - writeToken(); + writeParens(true); break; case tok!"@": case tok!"!": @@ -333,6 +357,10 @@ private: case tok!"$": writeToken(); break; + case tok!":": + write(" : "); + index += 1; + break; case tok!"]": writeToken(); if (current.type == tok!"identifier") @@ -341,7 +369,9 @@ private: case tok!";": tempIndent = 0; writeToken(); - if (current.type != tok!"comment") + if (index >= tokens.length || current.type != tok!"comment") + newline(); + if (peekImplementation(tok!"class",0)) newline(); break; case tok!"{": @@ -358,17 +388,14 @@ private: writeToken(); break; case tok!",": - if (currentLineLength + nextTokenLength() >= config.columnSoftLimit) + writeToken(); + if (currentLineLength + expressionLength() >= config.columnSoftLimit) { pushIndent(); - writeToken(); newline(); } else - { - writeToken(); write(" "); - } break; case tok!"^^": case tok!"^=": @@ -428,28 +455,63 @@ private: else if (current.type == tok!"identifier") { writeToken(); - if (current.type == tok!"identifier" || isKeyword(current.type)) + if (index < tokens.length && (current.type == tok!"identifier" + || isKeyword(current.type) || isBasicType(current.type) + || current.type == tok!"@")) + { write(" "); + } } else assert (false, str(current.type)); } - /// Pushes a temporary indent level + /// Pushes a temporary indent level void pushIndent() { if (tempIndent == 0) tempIndent++; } - /// Pops a temporary indent level + /// Pops a temporary indent level void popIndent() { if (tempIndent > 0) tempIndent--; } - /// Writes balanced braces + size_t expressionLength() const pure @safe @nogc + { + size_t i = index; + size_t l = 0; + int parenDepth = 0; + loop: while (i < tokens.length) switch (tokens[i].type) + { + case tok!"(": + parenDepth++; + l++; + i++; + break; + case tok!")": + parenDepth--; + if (parenDepth == 0) + break loop; + l++; + i++; + break; + case tok!";": + case tok!",": + break loop; + default: + l += tokenLength(i); + if (isBasicType(tokens[i].type) || tokens[i].type == tok!"identifier") + l++; + i++; + } + return l; + } + + /// Writes balanced braces void writeBraces() { import std.range : assumeSorted; @@ -480,11 +542,11 @@ private: newline(); write("}"); depth--; - if (index + 1 < tokens.length && + if (index < tokens.length - 1 && assumeSorted(astInformation.doubleNewlineLocations) .equalRange(tokens[index].index).length) { - output.write("\n"); + output.put("\n"); } if (config.braceStyle == BraceStyle.otbs) { @@ -513,7 +575,7 @@ private: popIndent(); } - void writeParens() + void writeParens(bool space_afterwards) in { assert (current.type == tok!"(", str(current.type)); @@ -540,9 +602,11 @@ private: else if (current.type == tok!")") { if (peekIs(tok!"identifier") || (index + 1 < tokens.length - && isKeyword(tokens[index + 1].type))) + && (isKeyword(tokens[index + 1].type) + || tokens[index + 1].type == tok!"@"))) { writeToken(); + if (space_afterwards) write(" "); } else @@ -567,7 +631,7 @@ private: immutable l = indentLevel; writeToken(); // switch write(" "); - writeParens(); + writeParens(true); if (current.type != tok!"{") return; if (config.braceStyle == BraceStyle.otbs) @@ -622,33 +686,38 @@ private: newline(); } - int currentTokenLength() - { - switch (current.type) - { - mixin (generateFixedLengthCases()); - default: return cast(int) current.text.length; - } - } - - int nextTokenLength() + int tokenLength(size_t i) const pure @safe @nogc { import std.algorithm : countUntil; - if (index + 1 >= tokens.length) - return INVALID_TOKEN_LENGTH; - auto nextToken = tokens[index + 1]; - switch (nextToken.type) + + assert(i + 1 <= tokens.length); + switch (tokens[i].type) { case tok!"identifier": case tok!"stringLiteral": case tok!"wstringLiteral": case tok!"dstringLiteral": - return cast(int) nextToken.text.countUntil('\n'); + auto c = cast(int) tokens[i].text.countUntil('\n'); + if (c == -1) + return cast(int) tokens[i].text.length; mixin (generateFixedLengthCases()); - default: return -1; + default : + return INVALID_TOKEN_LENGTH; } } + int currentTokenLength() pure @safe @nogc + { + return tokenLength(index); + } + + int nextTokenLength() pure @safe @nogc + { + if (index + 1 >= tokens.length) + return INVALID_TOKEN_LENGTH; + return tokenLength(index + 1); + } + ref current() const @property in { @@ -684,7 +753,7 @@ private: void newline() { - output.write("\n"); + output.put("\n"); currentLineLength = 0; if (index < tokens.length) { @@ -697,16 +766,16 @@ private: void write(string str) { currentLineLength += str.length; - output.write(str); + output.put(str); } void writeToken() { currentLineLength += currentTokenLength(); if (current.text is null) - output.write(str(current.type)); + output.put(str(current.type)); else - output.write(current.text); + output.put(current.text); index++; } @@ -717,13 +786,13 @@ private: foreach (i; 0 .. indentLevel + tempIndent) { currentLineLength += config.tabSize; - output.write("\t"); + output.put("\t"); } else foreach (i; 0 .. indentLevel + tempIndent) foreach (j; 0 .. config.indentSize) { - output.write(" "); + output.put(" "); currentLineLength++; } } @@ -743,8 +812,8 @@ private: /// Length of the current line (so far) uint currentLineLength = 0; - /// File to output to - File output; + /// Output to write output to + OutputRange output; /// Tokens being formatted const(Token)[] tokens; @@ -895,33 +964,32 @@ private: string generateFixedLengthCases() { - import std.algorithm:map; - import std.string:format; + import std.algorithm : map; + import std.string : format; - string[] fixedLengthTokens = [ - "abstract", "alias", "align", "asm", "assert", "auto", "body", "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", - "volatile", "wchar", "while", "with", "__DATE__", "__EOF__", "__FILE__", - "__FUNCTION__", "__gshared", "__LINE__", "__MODULE__", "__parameters", - "__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__", "__traits", "__vector", - "__VENDOR__", "__VERSION__", ",", ".", "..", "...", "/", "/=", "!", "!<", - "!<=", "!<>", "!<>=", "!=", "!>", "!>=", "$", "%", "%=", "&", "&&", "&=", - "(", ")", "*", "*=", "+", "++", "+=", "-", "--", "-=", ":", ";", "<", "<<", - "<<=", "<=", "<>", "<>=", "=", "==", "=>", ">", ">=", ">>", ">>=", ">>>", - ">>>=", "?", "@", "[", "]", "^", "^=", "^^", "^^=", "{", "|", "|=", "||", - "}", "~", "~=" - ]; - - return fixedLengthTokens.map!(a => format(`case tok!"%s": return %d;`, a, a.length)).join("\n\t"); + string[] fixedLengthTokens = ["abstract", "alias", "align", "asm", "assert", + "auto", "body", "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", "volatile", "wchar", + "while", "with", "__DATE__", "__EOF__", "__FILE__", "__FUNCTION__", + "__gshared", "__LINE__", "__MODULE__", "__parameters", + "__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__", "__traits", + "__vector", "__VENDOR__", "__VERSION__", ",", ".", "..", "...", "/", + "/=", "!", "!<", "!<=", "!<>", "!<>=", "!=", "!>", "!>=", "$", "%", "%=", + "&", "&&", "&=", "(", ")", "*", "*=", "+", "++", "+=", "-", "--", "-=", + ":", ";", "<", "<<", "<<=", "<=", "<>", "<>=", "=", "==", "=>", ">", + ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[", "]", "^", "^=", "^^", + "^^=", "{", "|", "|=", "||", "}", "~", "~="]; + return fixedLengthTokens.map!(a => format(`case tok!"%s": return %d;`, a, + a.length)).join("\n\t"); } diff --git a/tests/DeclSpacing.d b/tests/DeclSpacing.d new file mode 100644 index 0000000..a3af881 --- /dev/null +++ b/tests/DeclSpacing.d @@ -0,0 +1,6 @@ +import std.stdio; +class Foo {} +import std.conv; +void main() {return;} +const baz = 11; +class Foo2:Foo {} diff --git a/tests/DeclSpacing.d.ref b/tests/DeclSpacing.d.ref new file mode 100644 index 0000000..7383c47 --- /dev/null +++ b/tests/DeclSpacing.d.ref @@ -0,0 +1,18 @@ +import std.stdio; + +class Foo +{ +} + +import std.conv; + +void main() +{ + return; +} + +const baz = 11; + +class Foo2 : Foo +{ +} diff --git a/tests/catchExceptionNested.d b/tests/catchExceptionNested.d new file mode 100644 index 0000000..bddfd10 --- /dev/null +++ b/tests/catchExceptionNested.d @@ -0,0 +1,31 @@ +class U0 : Exception { + this() @safe pure nothrow { super("U0 error message"); } +} + +class U1 : Exception { + this() @safe pure nothrow { super("U1 error message"); } +} + +void foo() { + import std.stdio; + + foreach (immutable i; 0 .. 2) { + try { + i.bar; + } catch (U0) { + "Function foo caught exception U0".writeln; + } + } +} + +void bar(in int i) @safe pure { + i.baz; +} + +void baz(in int i) @safe pure { + throw i ? new U1 : new U0; +} + +void main() { + foo; +} diff --git a/tests/catchExceptionNested.d.ref b/tests/catchExceptionNested.d.ref new file mode 100644 index 0000000..22d8d26 --- /dev/null +++ b/tests/catchExceptionNested.d.ref @@ -0,0 +1,49 @@ +class U0 : Exception +{ + this() @safe pure nothrow + { + super("U0 error message"); + } + +} + +class U1 : Exception +{ + this() @safe pure nothrow + { + super("U1 error message"); + } + +} + +void foo() +{ + import std.stdio; + + foreach (immutable i; 0 .. 2) + { + try + { + i.bar; + } + catch(U0) + { + "Function foo caught exception U0".writeln; + } + } +} + +void bar(in int i) @safe pure +{ + i.baz; +} + +void baz(in int i) @safe pure +{ + throw i ? new U1 : new U0; +} + +void main() +{ + foo; +} diff --git a/tests/frontpage.d.ref b/tests/frontpage.d.ref index 640ab33..9d82c63 100644 --- a/tests/frontpage.d.ref +++ b/tests/frontpage.d.ref @@ -10,5 +10,5 @@ void main() ++lines; sumLength += line.length; } - writeln("Average line length: ", lines ? sumLength / lines:0); + writeln("Average line length: ", lines ? sumLength / lines : 0); } diff --git a/tests/guessnumber.d.ref b/tests/guessnumber.d.ref index 9ff51c1..7d8b62b 100644 --- a/tests/guessnumber.d.ref +++ b/tests/guessnumber.d.ref @@ -4,8 +4,7 @@ void main() { immutable interval = tuple(1, 100); writefln("Guess my target number that is between " - ~ "%d and %d (inclusive).\n", - interval[]); + ~ "%d and %d (inclusive).\n", interval[]); immutable target = uniform!"[]"(interval[]); foreach (immutable i; sequence!q{n}) { @@ -31,6 +30,6 @@ void main() writeln(" Well guessed."); break; } - writeln(answer < target ? " Too low.":" Too high."); + writeln(answer < target ? " Too low." : " Too high."); } } diff --git a/tests/heronian.d b/tests/heronian.d deleted file mode 100644 index ffe6334..0000000 --- a/tests/heronian.d +++ /dev/null @@ -1,45 +0,0 @@ -import std.stdio, std.math, std.range, std.algorithm, std.numeric, std.traits, std.typecons; - -double hero(in uint a, in uint b, in uint c) pure nothrow @safe @nogc { - immutable s = (a + b + c) / 2.0; - immutable a2 = s * (s - a) * (s - b) * (s - c); - return (a2 > 0) ? a2.sqrt : 0.0; -} - -bool isHeronian(in uint a, in uint b, in uint c) pure nothrow @safe @nogc { - immutable h = hero(a, b, c); - return h > 0 && h.floor == h.ceil; -} - -T gcd3(T)(in T x, in T y, in T z) pure nothrow @safe @nogc { - return gcd(gcd(x, y), z); -} - -void main() /*@safe*/ { - enum uint maxSide = 200; - - // Sort by increasing area, perimeter, then sides. - //auto h = cartesianProduct!3(iota(1, maxSide + 1)) - auto r = iota(1, maxSide + 1); - const h = cartesianProduct(r, r, r) - //.filter!({a, b, c} => ... - .filter!(t => t[0] <= t[1] && t[1] <= t[2] && - t[0] + t[1] > t[2] && - t[].gcd3 == 1 && t[].isHeronian) - .array - .schwartzSort!(t => tuple(t[].hero, t[].only.sum, t.reverse)) - .release; - - static void showTriangles(R)(R ts) @safe { - "Area Perimeter Sides".writeln; - foreach (immutable t; ts) - writefln("%3s %8d %3dx%dx%d", t[].hero, t[].only.sum, t[]); - } - - writefln("Primitive Heronian triangles with sides up to %d: %d", maxSide, h.length); - "\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:".writeln; - showTriangles(h.take(10)); - - "\nAll with area 210 subject to the previous ordering:".writeln; - showTriangles(h.filter!(t => t[].hero == 210)); -} diff --git a/tests/heronian.d.ref b/tests/heronian.d.ref deleted file mode 100644 index f7334ff..0000000 --- a/tests/heronian.d.ref +++ /dev/null @@ -1,48 +0,0 @@ -import std.stdio, std.math, std.range, std.algorithm, std.numeric, std.traits, - std.typecons; - -double hero(in uint a, in uint b, in uint c) pure nothrow @safe@nogc -{ - immutable s = (a + b + c) / 2.0; - immutable a2 = s * (s - a) * (s - b) * (s - c); - return (a2 > 0) ? a2.sqrt : 0.0; -} - -bool isHeronian(in uint a, in uint b, in uint c) pure nothrow @safe @nogc -{ - immutable h = hero(a, b, c); - return h > 0 && h.floor == h.ceil; -} - -T gcd3(T)(in T x, in T y, in T z) pure nothrow @safe @nogc -{ - return gcd(gcd(x, y), z); -} - -void main() /*@safe*/ -{ - enum uint maxSide = 200; - // Sort by increasing area, perimeter, then sides. - //auto h = cartesianProduct!3(iota(1, maxSide + 1)) - auto r = iota(1, maxSide + 1); - const h = cartesianProduct(r, r, r) - //.filter!({a, b, c} => ... - .filter!(t => t[0] <= t[1] && t[1] <= t[2] && t[0] + t[1] > t[2] && - t[].gcd3 == 1 && t[].isHeronian) - .array.schwartzSort!(t => tuple(t[].hero, t[].only.sum, t.reverse)) - .release; - - static void showTriangles(R)(R ts) @safe - { - "Area Perimeter Sides".writeln; - foreach (immutable t; ts) - writefln("%3s %8d %3dx%dx%d", t[].hero, t[].only.sum, t[]); - } - - writefln("Primitive Heronian triangles with sides up to %d: %d", maxSide, - h.length); - "\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:".writeln; - showTriangles(h.take(10)); - "\nAll with area 210 subject to the previous ordering:".writeln; - showTriangles(h.filter!(t => t[].hero == 210)); -} diff --git a/tests/propertySpacing.d b/tests/propertySpacing.d new file mode 100644 index 0000000..0dc8bcb --- /dev/null +++ b/tests/propertySpacing.d @@ -0,0 +1 @@ +@property double y(); diff --git a/tests/propertySpacing.d.ref b/tests/propertySpacing.d.ref new file mode 100644 index 0000000..0dc8bcb --- /dev/null +++ b/tests/propertySpacing.d.ref @@ -0,0 +1 @@ +@property double y(); diff --git a/tests/swap.d b/tests/swap.d new file mode 100644 index 0000000..d5ee7d3 --- /dev/null +++ b/tests/swap.d @@ -0,0 +1,24 @@ +import std.algorithm: swap; // from Phobos standard library + +// The D solution uses templates and it's similar to the C++ one: +void mySwap(T)(ref T left, ref T right) { + auto temp = left; + left = right; + right = temp; +} + +void main() { + import std.stdio; + + int[] a = [10, 20]; + writeln(a); + + // The std.algorithm standard library module + // contains a generic swap: + swap(a[0], a[1]); + writeln(a); + + // Using mySwap: + mySwap(a[0], a[1]); + writeln(a); +} diff --git a/tests/swap.d.ref b/tests/swap.d.ref new file mode 100644 index 0000000..a5176e3 --- /dev/null +++ b/tests/swap.d.ref @@ -0,0 +1,24 @@ +import std.algorithm : swap; // from Phobos standard library + +// The D solution uses templates and it's similar to the C++ one: +void mySwap(T)(ref T left, ref T right) +{ + auto temp = left; + left = right; + right = temp; +} + +void main() +{ + import std.stdio; + + int[] a = [10, 20]; + writeln(a); + // The std.algorithm standard library module + // contains a generic swap: + swap(a[0], a[1]); + writeln(a); + // Using mySwap: + mySwap(a[0], a[1]); + writeln(a); +} diff --git a/tests/test.sh b/tests/test.sh index ea46ff2..463159f 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -4,5 +4,5 @@ set -e for source in *.d do ../bin/dfmt "${source}" >"${source}.out" - diff -u "${source}.ref" "${source}.out" || echo "fail ${source}" + diff -u "${source}.ref" "${source}.out" done