Initial commit
This commit is contained in:
parent
139a43922d
commit
5d324e81be
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
*.o
|
||||
bin
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "libdparse"]
|
||||
path = libdparse
|
||||
url = https://github.com/Hackerpilot/libdparse.git
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 5d5c6b161e6b90621ac58c8402629b99ac7e83e1
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
Loading…
Reference in New Issue