From 5d324e81befecada0f4fdde12b55f520bf5d0f5c Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Sun, 11 Jan 2015 17:50:30 +0000 Subject: [PATCH] Initial commit --- .editorconfig | 7 + .gitignore | 2 + .gitmodules | 3 + libdparse | 1 + makefile | 8 + src/dfmt.d | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 495 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .gitmodules create mode 160000 libdparse create mode 100644 makefile create mode 100644 src/dfmt.d diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7a845c4 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b30a728 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +bin diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..86170fc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libdparse"] + path = libdparse + url = https://github.com/Hackerpilot/libdparse.git diff --git a/libdparse b/libdparse new file mode 160000 index 0000000..5d5c6b1 --- /dev/null +++ b/libdparse @@ -0,0 +1 @@ +Subproject commit 5d5c6b161e6b90621ac58c8402629b99ac7e83e1 diff --git a/makefile b/makefile new file mode 100644 index 0000000..e7f53a6 --- /dev/null +++ b/makefile @@ -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 + diff --git a/src/dfmt.d b/src/dfmt.d new file mode 100644 index 0000000..9bd1d22 --- /dev/null +++ b/src/dfmt.d @@ -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"); +}