Initial commit

This commit is contained in:
Hackerpilot 2015-01-11 17:50:30 +00:00
parent 139a43922d
commit 5d324e81be
6 changed files with 495 additions and 0 deletions

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
[*]
end_of_line = lf
insert_final_newline = true
indent_size = 4
tab_width = 8
trim_trailing_whitespace = true
indent_style = space

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
bin

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "libdparse"]
path = libdparse
url = https://github.com/Hackerpilot/libdparse.git

1
libdparse Submodule

@ -0,0 +1 @@
Subproject commit 5d5c6b161e6b90621ac58c8402629b99ac7e83e1

8
makefile Normal file
View File

@ -0,0 +1,8 @@
SRC := $(shell find src -name "*.d") $(shell find libdparse/src -name "*.d")
COMPILER := dmd
INCLUDE_PATHS := -Ilibdparse/src
FLAGS := -g -w $(INCLUDE_PATHS)
all: $(SRC)
$(COMPILER) $(FLAGS) $(SRC) -ofbin/dfmt

474
src/dfmt.d Normal file
View File

@ -0,0 +1,474 @@
/*******************************************************************************
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
******************************************************************************/
module dfmt;
import std.stdio;
import std.d.lexer;
import std.d.parser;
import std.d.formatter;
import std.array;
int main(string[] args)
{
if (args.length < 2)
{
writeln("File name is a required argument");
return 1;
}
File f = File(args[1]);
ubyte[] buffer = new ubyte[](f.size);
f.rawRead(buffer);
LexerConfig config;
config.stringBehavior = StringBehavior.source;
config.whitespaceBehavior = WhitespaceBehavior.skip;
StringCache cache = StringCache(StringCache.defaultBucketCount);
auto tokens = byToken(buffer, config, &cache).array();
auto tokenFormatter = TokenFormatter(tokens, stdout);
tokenFormatter.format();
return 0;
}
struct TokenFormatter
{
this(const(Token)[] tokens, File output)
{
this.tokens = tokens;
this.output = output;
}
void format()
{
while (index < tokens.length)
formatStep();
}
invariant
{
assert (indentLevel >= 0);
}
void formatStep()
{
assert (index < tokens.length);
if (current.type == tok!"comment")
{
writeToken();
newline();
}
else if (isStringLiteral(current.type) || isNumberLiteral(current.type))
{
writeToken();
}
else if (current.type == tok!"case" || current.type == tok!"default")
{
if (current.type == tok!"case")
{
writeToken();
write(" ");
}
else
writeToken();
auto i = indentLevel;
int braceLevel = 0;
while (true)
{
if (current.type == tok!":")
{
if (peekIs(tok!"case") || peekIs(tok!"default"))
{
indentLevel = i;
writeToken();
newline();
break;
}
else
{
indentLevel++;
writeToken();
newline();
}
}
else if (peekIs(tok!"case") || peekIs(tok!"default"))
{
indentLevel = i;
break;
}
else if (current.type == tok!"{") braceLevel++;
else if (peekIs(tok!"}"))
{
braceLevel--;
if (braceLevel < 0)
{
indentLevel = i;
break;
}
}
formatStep();
}
}
else if (current.type == tok!"if" || current.type == tok!"while" || current.type == tok!"for"
|| current.type == tok!"foreach" || current.type == tok!"foreach_reverse")
{
currentLineLength += currentTokenLength() + 1;
writeToken();
write(" ");
int parenMatch = 0;
do
{
if (current.type == tok!";")
{
write("; ");
currentLineLength += 2;
index++;
continue;
}
else if (current.type == tok!"(")
parenMatch++;
else if (current.type == tok!")")
parenMatch--;
formatStep();
}
while (parenMatch > 0);
if (current.type != tok!"{" && current.type != tok!";")
{
indentLevel++;
newline();
deIndentOnNewline++;
}
}
else if (isKeyword(current.type))
{
if (current.type != tok!"default" && current.type != tok!"cast"
&& !peekIs(tok!"."))
{
writeToken();
write(" ");
}
else
writeToken();
}
else if (isBasicType(current.type))
{
writeToken();
if (current.type == tok!"identifier")
write(" ");
}
else if (isOperator(current.type))
{
switch (current.type)
{
case tok!"*":
case tok!"~":
case tok!"&":
case tok!"+":
case tok!"-":
// TODO: unary
case tok!"@":
case tok!"!":
case tok!".":
case tok!"...":
case tok!"(":
case tok!")":
case tok!"[":
case tok!"++":
case tok!"--":
case tok!"$":
writeToken();
break;
case tok!"]":
writeToken();
if (current.type == tok!"identifier")
write(" ");
break;
case tok!";":
writeToken();
newline();
break;
case tok!"{":
if (otbs)
write(" {");
else
{
newline();
write("{");
}
index++;
indentLevel++;
newline();
break;
case tok!"}":
write("}");
index++;
if (otbs && current.type == tok!"else")
write(" ");
else
newline();
break;
case tok!",":
if (currentLineLength + nextTokenLength() >= columnSoftLimit)
{
write(",");
newline();
}
else
write(", ");
index++;
break;
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!":":
if (currentLineLength + nextTokenLength() >= columnSoftLimit)
newline();
else
write(" ");
writeToken();
write(" ");
break;
default:
assert (false, str(current.type));
}
}
else if (current.type == tok!"identifier")
{
writeToken();
if (current.type == tok!"identifier")
write(" ");
}
else
index++;
}
int currentTokenLength()
{
switch (current.type)
{
mixin (generateFixedLengthCases());
default: return cast(int) current.text.length;
}
}
int nextTokenLength()
{
if (index + 1 >= tokens.length)
return INVALID_TOKEN_LENGTH;
auto nextToken = tokens[index + 1];
switch (nextToken.type)
{
case tok!"identifier":
case tok!"stringLiteral":
case tok!"wstringLiteral":
case tok!"dstringLiteral":
return cast(int) nextToken.text.length;
mixin (generateFixedLengthCases());
default: return -1;
}
}
ref current() const @property
in
{
assert (index < tokens.length);
}
body
{
return tokens[index];
}
bool peekBackIs(IdType tokenType)
{
return (index >= 1) && tokens[index - 1].type == tokenType;
}
bool peekIs(IdType tokenType)
{
auto i = index + 1;
while (i < tokens.length && tokens[i].type == tok!"comment")
i++;
return i < tokens.length && tokens[i].type == tokenType;
}
void newline()
{
output.write("\n");
currentLineLength = 0;
if (deIndentOnNewline)
{
deIndentOnNewline--;
indentLevel--;
}
if (index < tokens.length)
{
if (current.type == tok!"}")
indentLevel--;
indent();
}
}
void write(string str)
{
currentLineLength += str.length;
output.write(str);
}
void writeToken()
{
currentLineLength += currentTokenLength();
if (current.text is null)
output.write(str(current.type));
else
output.write(current.text);
index++;
}
void indent()
{
import std.range : repeat, take;
if (useTabs)
foreach (i; 0 .. indentLevel)
{
currentLineLength += tabSize;
output.write("\t");
}
else
foreach (i; 0 .. indentLevel)
foreach (j; 0 .. indentSize)
{
output.write(" ");
currentLineLength++;
}
}
/// Length of an invalid token
enum int INVALID_TOKEN_LENGTH = -1;
/// Current index into the tokens array
size_t index;
/// Current indent level
int indentLevel;
/// Number of spaces used for indentation
uint indentSize = 4;
/// Use tabs or spaces
bool useTabs = false;
/// Size of a tab character
uint tabSize = 8;
/// Soft line wrap limit
uint columnSoftLimit = 80;
/// Hard line wrap limit
uint columnHardLimit = 120;
/// Length of the current line (so far)
uint currentLineLength = 0;
/// Use the One True Brace Style
bool otbs = false;
int deIndentOnNewline = 0;
/// File to output to
File output;
/// Tokens being formatted
const(Token)[] tokens;
}
string generateFixedLengthCases()
{
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");
}