Conflicts:
	src/dfmt.d
This commit is contained in:
Hackerpilot 2015-02-17 12:43:42 -08:00
commit 506e150eea
18 changed files with 376 additions and 224 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ bin
*.d.out *.d.out
.dub .dub
dfmt dfmt
dub.selections.json

2
.travis.yml Normal file
View File

@ -0,0 +1,2 @@
language: d
script: make test

View File

@ -1,2 +1,23 @@
# dfmt # 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.

@ -1 +1 @@
Subproject commit bce47174cc2ea6cfd26d007a9c41108e4fb28f0e Subproject commit f73bed9279602a228933b21156d8f1f2ec5e4fdd

View File

@ -34,37 +34,24 @@ import std.d.formatter;
import std.d.ast; import std.d.ast;
import std.array; import std.array;
/// Help text version (NoMain)
immutable USAGE = "usage: %s [--inplace] [<path>...] { }
Formats D code. else
--inplace change file in-place instead of outputing to stdout
(implicit in case of multiple files)
-h, --help display this help and exit
";
int main(string[] args) int main(string[] args)
{ {
import std.getopt : getopt; import std.getopt : getopt;
import std.path: baseName;
import std.file : isDir, dirEntries, SpanMode;
bool inplace = false; bool inplace = false;
bool show_usage = false; bool show_usage = false;
FormatterConfig formatterConfig;
try
{
getopt(args, getopt(args,
"help|h", &show_usage, "help|h", &show_usage,
"inplace", &inplace); "inplace", &inplace,
} "tabs|t", &formatterConfig.useTabs,
catch (Exception e) "braces", &formatterConfig.braceStyle);
{
writef(USAGE, baseName(args[0]));
return 1;
}
if (show_usage) if (show_usage)
{ {
import std.path: baseName;
writef(USAGE, baseName(args[0])); writef(USAGE, baseName(args[0]));
return 0; return 0;
} }
@ -83,10 +70,11 @@ int main(string[] args)
else else
break; break;
} }
format("stdin", buffer, output); format("stdin", buffer, output.lockingTextWriter(), &formatterConfig);
} }
else else
{ {
import std.file : dirEntries, isDir, SpanMode;
if (args.length >= 2) if (args.length >= 2)
inplace = true; inplace = true;
while (args.length > 0) while (args.length > 0)
@ -107,19 +95,27 @@ int main(string[] args)
f.rawRead(buffer); f.rawRead(buffer);
if (inplace) if (inplace)
output = File(path, "w"); output = File(path, "w");
format(path, buffer, output); format(path, buffer, output.lockingTextWriter(), &formatterConfig);
} }
} }
return 0; return 0;
} }
/** private:
* Params:
* source_desc = immutable USAGE = "usage: %s [--inplace] [<path>...]
* buffer = Formats D code.
* output =
*/ --inplace Change file in-place instead of outputing to stdout
void format(string source_desc, ubyte[] buffer, File output) (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; LexerConfig config;
config.stringBehavior = StringBehavior.source; config.stringBehavior = StringBehavior.source;
@ -129,29 +125,27 @@ void format(string source_desc, ubyte[] buffer, File output)
parseConfig.whitespaceBehavior = WhitespaceBehavior.skip; parseConfig.whitespaceBehavior = WhitespaceBehavior.skip;
StringCache cache = StringCache(StringCache.defaultBucketCount); StringCache cache = StringCache(StringCache.defaultBucketCount);
ASTInformation astInformation; ASTInformation astInformation;
FormatterConfig formatterConfig;
auto parseTokens = getTokensForParser(buffer, parseConfig, &cache); auto parseTokens = getTokensForParser(buffer, parseConfig, &cache);
auto mod = parseModule(parseTokens, source_desc); auto mod = parseModule(parseTokens, source_desc);
auto visitor = new FormatVisitor(&astInformation); auto visitor = new FormatVisitor(&astInformation);
visitor.visit(mod); visitor.visit(mod);
astInformation.cleanup(); astInformation.cleanup();
auto tokens = byToken(buffer, config, &cache).array(); auto tokens = byToken(buffer, config, &cache).array();
auto tokenFormatter = TokenFormatter(tokens, output, &astInformation, auto tokenFormatter = TokenFormatter!OutputRange(tokens, output, &astInformation,
&formatterConfig); formatterConfig);
tokenFormatter.format(); tokenFormatter.format();
} }
/// Contains formatting logic struct TokenFormatter(OutputRange)
struct TokenFormatter
{ {
/** /**
* Params: * Params:
* tokens = the tokens to format * 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 * astInformation = information about the AST used to inform formatting
* decisions. * decisions.
*/ */
this(const(Token)[] tokens, File output, ASTInformation* astInformation, this(const(Token)[] tokens, OutputRange output, ASTInformation* astInformation,
FormatterConfig* config) FormatterConfig* config)
{ {
this.tokens = tokens; this.tokens = tokens;
@ -184,7 +178,7 @@ private:
const i = index; const i = index;
if (i > 0) 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" if (tokens[i-1].type != tok!"comment"
&& tokens[i-1].type != tok!"{") && tokens[i-1].type != tok!"{")
@ -194,11 +188,16 @@ private:
write(" "); write(" ");
} }
writeToken(); writeToken();
if (i + 1 >= tokens.length) if (i >= tokens.length-1)
newline(); newline();
else if (tokens[i + 1].line > tokens[i].line) else if (tokens[i+1].line-1 > tokens[i].line)
{
newline(); 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(" "); write(" ");
} }
else if (isStringLiteral(current.type) || isNumberLiteral(current.type) else if (isStringLiteral(current.type) || isNumberLiteral(current.type)
@ -217,11 +216,40 @@ private:
{ {
writeToken(); writeToken();
tempIndent = 0; tempIndent = 0;
if (index >= tokens.length)
{
newline();
break;
}
if (current.type == tok!"comment")
break;
if (!(t == tok!"import" && current.type == tok!"import")) if (!(t == tok!"import" && current.type == tok!"import"))
write("\n"); write("\n");
newline(); newline();
break; 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<tokens.length; i++)
{
if (tokens[i].type == tok!"," || tokens[i].type == tok!";")
break;
const len = tokenLength(i);
assert (len >= 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 else
formatStep(); formatStep();
} }
@ -229,10 +257,17 @@ private:
else if (current.type == tok!"return") else if (current.type == tok!"return")
{ {
writeToken(); writeToken();
if (current.type != tok!";")
write(" "); write(" ");
} }
else if (current.type == tok!"switch") else if (current.type == tok!"switch")
formatSwitch(); formatSwitch();
else if (current.type == tok!"version" && peekIs(tok!"("))
{
writeToken();
write(" ");
writeParens(false);
}
else if (current.type == tok!"for" || current.type == tok!"foreach" else if (current.type == tok!"for" || current.type == tok!"foreach"
|| current.type == tok!"foreach_reverse" || current.type == tok!"while" || current.type == tok!"foreach_reverse" || current.type == tok!"while"
|| current.type == tok!"if") || current.type == tok!"if")
@ -240,7 +275,7 @@ private:
currentLineLength += currentTokenLength() + 1; currentLineLength += currentTokenLength() + 1;
writeToken(); writeToken();
write(" "); write(" ");
writeParens(); writeParens(false);
if (current.type != tok!"{" && current.type != tok!";") if (current.type != tok!"{" && current.type != tok!";")
{ {
pushIndent(); pushIndent();
@ -311,18 +346,7 @@ private:
} }
goto binary; goto binary;
case tok!"(": case tok!"(":
writeParens(); writeParens(true);
break;
case tok!":":
if (!assumeSorted(astInformation.ternaryColonLocations)
.equalRange(current.index).empty)
{
write(" ");
writeToken();
write(" ");
}
else
writeToken();
break; break;
case tok!"@": case tok!"@":
case tok!"!": case tok!"!":
@ -333,6 +357,10 @@ private:
case tok!"$": case tok!"$":
writeToken(); writeToken();
break; break;
case tok!":":
write(" : ");
index += 1;
break;
case tok!"]": case tok!"]":
writeToken(); writeToken();
if (current.type == tok!"identifier") if (current.type == tok!"identifier")
@ -341,7 +369,9 @@ private:
case tok!";": case tok!";":
tempIndent = 0; tempIndent = 0;
writeToken(); writeToken();
if (current.type != tok!"comment") if (index >= tokens.length || current.type != tok!"comment")
newline();
if (peekImplementation(tok!"class",0))
newline(); newline();
break; break;
case tok!"{": case tok!"{":
@ -358,17 +388,14 @@ private:
writeToken(); writeToken();
break; break;
case tok!",": case tok!",":
if (currentLineLength + nextTokenLength() >= config.columnSoftLimit) writeToken();
if (currentLineLength + expressionLength() >= config.columnSoftLimit)
{ {
pushIndent(); pushIndent();
writeToken();
newline(); newline();
} }
else else
{
writeToken();
write(" "); write(" ");
}
break; break;
case tok!"^^": case tok!"^^":
case tok!"^=": case tok!"^=":
@ -428,9 +455,13 @@ private:
else if (current.type == tok!"identifier") else if (current.type == tok!"identifier")
{ {
writeToken(); 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(" "); write(" ");
} }
}
else else
assert (false, str(current.type)); assert (false, str(current.type));
} }
@ -449,6 +480,37 @@ private:
tempIndent--; tempIndent--;
} }
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 /// Writes balanced braces
void writeBraces() void writeBraces()
{ {
@ -480,11 +542,11 @@ private:
newline(); newline();
write("}"); write("}");
depth--; depth--;
if (index + 1 < tokens.length && if (index < tokens.length - 1 &&
assumeSorted(astInformation.doubleNewlineLocations) assumeSorted(astInformation.doubleNewlineLocations)
.equalRange(tokens[index].index).length) .equalRange(tokens[index].index).length)
{ {
output.write("\n"); output.put("\n");
} }
if (config.braceStyle == BraceStyle.otbs) if (config.braceStyle == BraceStyle.otbs)
{ {
@ -513,7 +575,7 @@ private:
popIndent(); popIndent();
} }
void writeParens() void writeParens(bool space_afterwards)
in in
{ {
assert (current.type == tok!"(", str(current.type)); assert (current.type == tok!"(", str(current.type));
@ -540,9 +602,11 @@ private:
else if (current.type == tok!")") else if (current.type == tok!")")
{ {
if (peekIs(tok!"identifier") || (index + 1 < tokens.length if (peekIs(tok!"identifier") || (index + 1 < tokens.length
&& isKeyword(tokens[index + 1].type))) && (isKeyword(tokens[index + 1].type)
|| tokens[index + 1].type == tok!"@")))
{ {
writeToken(); writeToken();
if (space_afterwards)
write(" "); write(" ");
} }
else else
@ -567,7 +631,7 @@ private:
immutable l = indentLevel; immutable l = indentLevel;
writeToken(); // switch writeToken(); // switch
write(" "); write(" ");
writeParens(); writeParens(true);
if (current.type != tok!"{") if (current.type != tok!"{")
return; return;
if (config.braceStyle == BraceStyle.otbs) if (config.braceStyle == BraceStyle.otbs)
@ -622,33 +686,38 @@ private:
newline(); newline();
} }
int currentTokenLength() int tokenLength(size_t i) const pure @safe @nogc
{
switch (current.type)
{
mixin (generateFixedLengthCases());
default: return cast(int) current.text.length;
}
}
int nextTokenLength()
{ {
import std.algorithm : countUntil; import std.algorithm : countUntil;
if (index + 1 >= tokens.length)
return INVALID_TOKEN_LENGTH; assert(i + 1 <= tokens.length);
auto nextToken = tokens[index + 1]; switch (tokens[i].type)
switch (nextToken.type)
{ {
case tok!"identifier": case tok!"identifier":
case tok!"stringLiteral": case tok!"stringLiteral":
case tok!"wstringLiteral": case tok!"wstringLiteral":
case tok!"dstringLiteral": 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()); 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 ref current() const @property
in in
{ {
@ -684,7 +753,7 @@ private:
void newline() void newline()
{ {
output.write("\n"); output.put("\n");
currentLineLength = 0; currentLineLength = 0;
if (index < tokens.length) if (index < tokens.length)
{ {
@ -697,16 +766,16 @@ private:
void write(string str) void write(string str)
{ {
currentLineLength += str.length; currentLineLength += str.length;
output.write(str); output.put(str);
} }
void writeToken() void writeToken()
{ {
currentLineLength += currentTokenLength(); currentLineLength += currentTokenLength();
if (current.text is null) if (current.text is null)
output.write(str(current.type)); output.put(str(current.type));
else else
output.write(current.text); output.put(current.text);
index++; index++;
} }
@ -717,13 +786,13 @@ private:
foreach (i; 0 .. indentLevel + tempIndent) foreach (i; 0 .. indentLevel + tempIndent)
{ {
currentLineLength += config.tabSize; currentLineLength += config.tabSize;
output.write("\t"); output.put("\t");
} }
else else
foreach (i; 0 .. indentLevel + tempIndent) foreach (i; 0 .. indentLevel + tempIndent)
foreach (j; 0 .. config.indentSize) foreach (j; 0 .. config.indentSize)
{ {
output.write(" "); output.put(" ");
currentLineLength++; currentLineLength++;
} }
} }
@ -743,8 +812,8 @@ private:
/// Length of the current line (so far) /// Length of the current line (so far)
uint currentLineLength = 0; uint currentLineLength = 0;
/// File to output to /// Output to write output to
File output; OutputRange output;
/// Tokens being formatted /// Tokens being formatted
const(Token)[] tokens; const(Token)[] tokens;
@ -895,33 +964,32 @@ private:
string generateFixedLengthCases() string generateFixedLengthCases()
{ {
import std.algorithm:map; import std.algorithm : map;
import std.string:format; import std.string : format;
string[] fixedLengthTokens = [ string[] fixedLengthTokens = ["abstract", "alias", "align", "asm", "assert",
"abstract", "alias", "align", "asm", "assert", "auto", "body", "bool", "auto", "body", "bool", "break", "byte", "case", "cast", "catch",
"break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", "cdouble", "cent", "cfloat", "char", "class", "const", "continue",
"char", "class", "const", "continue", "creal", "dchar", "debug", "default", "creal", "dchar", "debug", "default", "delegate", "delete", "deprecated",
"delegate", "delete", "deprecated", "do", "double", "else", "enum", "do", "double", "else", "enum", "export", "extern", "false", "final",
"export", "extern", "false", "final", "finally", "float", "for", "foreach", "finally", "float", "for", "foreach", "foreach_reverse", "function",
"foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "goto", "idouble", "if", "ifloat", "immutable", "import", "in", "inout",
"immutable", "import", "in", "inout", "int", "interface", "invariant", "int", "interface", "invariant", "ireal", "is", "lazy", "long", "macro",
"ireal", "is", "lazy", "long", "macro", "mixin", "module", "new", "nothrow", "mixin", "module", "new", "nothrow", "null", "out", "override",
"null", "out", "override", "package", "pragma", "private", "protected", "package", "pragma", "private", "protected", "public", "pure", "real",
"public", "pure", "real", "ref", "return", "scope", "shared", "short", "ref", "return", "scope", "shared", "short", "static", "struct", "super",
"static", "struct", "super", "switch", "synchronized", "template", "this", "switch", "synchronized", "template", "this", "throw", "true", "try",
"throw", "true", "try", "typedef", "typeid", "typeof", "ubyte", "ucent", "typedef", "typeid", "typeof", "ubyte", "ucent", "uint", "ulong",
"uint", "ulong", "union", "unittest", "ushort", "version", "void", "union", "unittest", "ushort", "version", "void", "volatile", "wchar",
"volatile", "wchar", "while", "with", "__DATE__", "__EOF__", "__FILE__", "while", "with", "__DATE__", "__EOF__", "__FILE__", "__FUNCTION__",
"__FUNCTION__", "__gshared", "__LINE__", "__MODULE__", "__parameters", "__gshared", "__LINE__", "__MODULE__", "__parameters",
"__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__", "__traits", "__vector", "__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__", "__traits",
"__VENDOR__", "__VERSION__", ",", ".", "..", "...", "/", "/=", "!", "!<", "__vector", "__VENDOR__", "__VERSION__", ",", ".", "..", "...", "/",
"!<=", "!<>", "!<>=", "!=", "!>", "!>=", "$", "%", "%=", "&", "&&", "&=", "/=", "!", "!<", "!<=", "!<>", "!<>=", "!=", "!>", "!>=", "$", "%", "%=",
"(", ")", "*", "*=", "+", "++", "+=", "-", "--", "-=", ":", ";", "<", "<<", "&", "&&", "&=", "(", ")", "*", "*=", "+", "++", "+=", "-", "--", "-=",
"<<=", "<=", "<>", "<>=", "=", "==", "=>", ">", ">=", ">>", ">>=", ">>>", ":", ";", "<", "<<", "<<=", "<=", "<>", "<>=", "=", "==", "=>", ">",
">>>=", "?", "@", "[", "]", "^", "^=", "^^", "^^=", "{", "|", "|=", "||", ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[", "]", "^", "^=", "^^",
"}", "~", "~=" "^^=", "{", "|", "|=", "||", "}", "~", "~="];
]; return fixedLengthTokens.map!(a => format(`case tok!"%s": return %d;`, a,
a.length)).join("\n\t");
return fixedLengthTokens.map!(a => format(`case tok!"%s": return %d;`, a, a.length)).join("\n\t");
} }

6
tests/DeclSpacing.d Normal file
View File

@ -0,0 +1,6 @@
import std.stdio;
class Foo {}
import std.conv;
void main() {return;}
const baz = 11;
class Foo2:Foo {}

18
tests/DeclSpacing.d.ref Normal file
View File

@ -0,0 +1,18 @@
import std.stdio;
class Foo
{
}
import std.conv;
void main()
{
return;
}
const baz = 11;
class Foo2 : Foo
{
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -10,5 +10,5 @@ void main()
++lines; ++lines;
sumLength += line.length; sumLength += line.length;
} }
writeln("Average line length: ", lines ? sumLength / lines:0); writeln("Average line length: ", lines ? sumLength / lines : 0);
} }

View File

@ -4,8 +4,7 @@ void main()
{ {
immutable interval = tuple(1, 100); immutable interval = tuple(1, 100);
writefln("Guess my target number that is between " writefln("Guess my target number that is between "
~ "%d and %d (inclusive).\n", ~ "%d and %d (inclusive).\n", interval[]);
interval[]);
immutable target = uniform!"[]"(interval[]); immutable target = uniform!"[]"(interval[]);
foreach (immutable i; sequence!q{n}) foreach (immutable i; sequence!q{n})
{ {
@ -31,6 +30,6 @@ void main()
writeln(" Well guessed."); writeln(" Well guessed.");
break; break;
} }
writeln(answer < target ? " Too low.":" Too high."); writeln(answer < target ? " Too low." : " Too high.");
} }
} }

View File

@ -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));
}

View File

@ -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));
}

1
tests/propertySpacing.d Normal file
View File

@ -0,0 +1 @@
@property double y();

View File

@ -0,0 +1 @@
@property double y();

24
tests/swap.d Normal file
View File

@ -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);
}

24
tests/swap.d.ref Normal file
View File

@ -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);
}

View File

@ -4,5 +4,5 @@ set -e
for source in *.d for source in *.d
do do
../bin/dfmt "${source}" >"${source}.out" ../bin/dfmt "${source}" >"${source}.out"
diff -u "${source}.ref" "${source}.out" || echo "fail ${source}" diff -u "${source}.ref" "${source}.out"
done done