Silly experiment
This commit is contained in:
parent
52d088d934
commit
a886dc5cd0
2
makefile
2
makefile
|
@ -1,6 +1,6 @@
|
||||||
SRC := $(shell find src -name "*.d") $(shell find libdparse/src -name "*.d")
|
SRC := $(shell find src -name "*.d") $(shell find libdparse/src -name "*.d")
|
||||||
INCLUDE_PATHS := -Ilibdparse/src
|
INCLUDE_PATHS := -Ilibdparse/src
|
||||||
DMD_FLAGS := -g -w $(INCLUDE_PATHS)
|
DMD_FLAGS := -g -w -unittest $(INCLUDE_PATHS)
|
||||||
LDC_FLAGS := -g -w -oq $(INCLUDE_PATHS)
|
LDC_FLAGS := -g -w -oq $(INCLUDE_PATHS)
|
||||||
|
|
||||||
.PHONY: dmd ldc test
|
.PHONY: dmd ldc test
|
||||||
|
|
329
src/dfmt.d
329
src/dfmt.d
|
@ -233,7 +233,7 @@ private:
|
||||||
{
|
{
|
||||||
if (tokens[i].type == tok!"," || tokens[i].type == tok!";")
|
if (tokens[i].type == tok!"," || tokens[i].type == tok!";")
|
||||||
break;
|
break;
|
||||||
const len = tokenLength(i);
|
const len = tokenLength(tokens[i]);
|
||||||
assert (len >= 0);
|
assert (len >= 0);
|
||||||
length_of_next_chunk += len;
|
length_of_next_chunk += len;
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,7 @@ private:
|
||||||
break;
|
break;
|
||||||
case tok!",":
|
case tok!",":
|
||||||
writeToken();
|
writeToken();
|
||||||
if (currentLineLength + expressionLength() >= config.columnSoftLimit)
|
if (currentLineLength + distanceToNextPreferredBreak() >= config.columnSoftLimit)
|
||||||
{
|
{
|
||||||
pushIndent();
|
pushIndent();
|
||||||
newline();
|
newline();
|
||||||
|
@ -405,6 +405,18 @@ private:
|
||||||
else
|
else
|
||||||
write(" ");
|
write(" ");
|
||||||
break;
|
break;
|
||||||
|
case tok!"=":
|
||||||
|
case tok!">=":
|
||||||
|
case tok!">>=":
|
||||||
|
case tok!">>>=":
|
||||||
|
case tok!"|=":
|
||||||
|
case tok!"-=":
|
||||||
|
case tok!"/=":
|
||||||
|
case tok!"*=":
|
||||||
|
case tok!"&=":
|
||||||
|
case tok!"%=":
|
||||||
|
case tok!"+=":
|
||||||
|
goto case;
|
||||||
case tok!"^^":
|
case tok!"^^":
|
||||||
case tok!"^=":
|
case tok!"^=":
|
||||||
case tok!"^":
|
case tok!"^":
|
||||||
|
@ -417,17 +429,11 @@ private:
|
||||||
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!"!<>":
|
||||||
|
@ -436,15 +442,10 @@ private:
|
||||||
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!"&&":
|
||||||
binary:
|
binary:
|
||||||
if (currentLineLength + distanceToNextPreferredBreak() >= config.columnSoftLimit)
|
if (currentLineLength + distanceToNextPreferredBreak() >= config.columnSoftLimit)
|
||||||
{
|
{
|
||||||
|
@ -488,33 +489,29 @@ private:
|
||||||
tempIndent--;
|
tempIndent--;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t expressionLength() const pure @safe @nogc
|
size_t expressionEndIndex() const pure @safe @nogc
|
||||||
{
|
{
|
||||||
size_t i = index;
|
size_t i = index;
|
||||||
size_t l = 0;
|
|
||||||
int parenDepth = 0;
|
int parenDepth = 0;
|
||||||
loop: while (i < tokens.length) switch (tokens[i].type)
|
loop: while (i < tokens.length) switch (tokens[i].type)
|
||||||
{
|
{
|
||||||
case tok!"(":
|
case tok!"(":
|
||||||
parenDepth++;
|
parenDepth++;
|
||||||
l++;
|
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
case tok!")":
|
case tok!")":
|
||||||
parenDepth--;
|
parenDepth--;
|
||||||
if (parenDepth <= 0)
|
if (parenDepth <= 0)
|
||||||
break loop;
|
break loop;
|
||||||
l++;
|
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
case tok!";":
|
case tok!";":
|
||||||
case tok!",":
|
|
||||||
break loop;
|
break loop;
|
||||||
default:
|
default:
|
||||||
l += tokenLength(i);
|
|
||||||
i++;
|
i++;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return l;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes balanced braces
|
/// Writes balanced braces
|
||||||
|
@ -709,30 +706,9 @@ private:
|
||||||
newline();
|
newline();
|
||||||
}
|
}
|
||||||
|
|
||||||
int tokenLength(size_t i) const pure @safe @nogc
|
|
||||||
{
|
|
||||||
import std.algorithm : countUntil;
|
|
||||||
|
|
||||||
assert(i + 1 <= tokens.length);
|
|
||||||
switch (tokens[i].type)
|
|
||||||
{
|
|
||||||
case tok!"identifier":
|
|
||||||
case tok!"stringLiteral":
|
|
||||||
case tok!"wstringLiteral":
|
|
||||||
case tok!"dstringLiteral":
|
|
||||||
// TODO: Unicode line breaks and old-Mac line endings
|
|
||||||
auto c = cast(int) tokens[i].text.countUntil('\n');
|
|
||||||
if (c == -1)
|
|
||||||
return cast(int) tokens[i].text.length;
|
|
||||||
mixin (generateFixedLengthCases());
|
|
||||||
default :
|
|
||||||
return INVALID_TOKEN_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentTokenLength() pure @safe @nogc
|
int currentTokenLength() pure @safe @nogc
|
||||||
{
|
{
|
||||||
return tokenLength(index);
|
return tokenLength(tokens[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextTokenLength() pure @safe @nogc
|
int nextTokenLength() pure @safe @nogc
|
||||||
|
@ -740,7 +716,7 @@ private:
|
||||||
immutable size_t i = index + 1;
|
immutable size_t i = index + 1;
|
||||||
if (i >= tokens.length)
|
if (i >= tokens.length)
|
||||||
return INVALID_TOKEN_LENGTH;
|
return INVALID_TOKEN_LENGTH;
|
||||||
return tokenLength(i);
|
return tokenLength(tokens[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int distanceToNextPreferredBreak() pure @safe @nogc
|
int distanceToNextPreferredBreak() pure @safe @nogc
|
||||||
|
@ -757,7 +733,7 @@ private:
|
||||||
case tok!"(":
|
case tok!"(":
|
||||||
break loop;
|
break loop;
|
||||||
default:
|
default:
|
||||||
l += tokenLength(i);
|
l += tokenLength(tokens[i]);
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -849,9 +825,6 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Length of an invalid token
|
|
||||||
enum int INVALID_TOKEN_LENGTH = -1;
|
|
||||||
|
|
||||||
/// Current index into the tokens array
|
/// Current index into the tokens array
|
||||||
size_t index;
|
size_t index;
|
||||||
|
|
||||||
|
@ -1009,6 +982,9 @@ private:
|
||||||
alias visit = ASTVisitor.visit;
|
alias visit = ASTVisitor.visit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Length of an invalid token
|
||||||
|
enum int INVALID_TOKEN_LENGTH = -1;
|
||||||
|
|
||||||
string generateFixedLengthCases()
|
string generateFixedLengthCases()
|
||||||
{
|
{
|
||||||
import std.algorithm : map;
|
import std.algorithm : map;
|
||||||
|
@ -1040,3 +1016,260 @@ string generateFixedLengthCases()
|
||||||
return fixedLengthTokens.map!(a => format(`case tok!"%s": return %d;`, a,
|
return fixedLengthTokens.map!(a => format(`case tok!"%s": return %d;`, a,
|
||||||
a.length)).join("\n\t");
|
a.length)).join("\n\t");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tokenLength(ref const Token t) pure @safe @nogc
|
||||||
|
{
|
||||||
|
import std.algorithm : countUntil;
|
||||||
|
switch (t.type)
|
||||||
|
{
|
||||||
|
case tok!"identifier":
|
||||||
|
case tok!"stringLiteral":
|
||||||
|
case tok!"wstringLiteral":
|
||||||
|
case tok!"dstringLiteral":
|
||||||
|
// TODO: Unicode line breaks and old-Mac line endings
|
||||||
|
auto c = cast(int) t.text.countUntil('\n');
|
||||||
|
if (c == -1)
|
||||||
|
return cast(int) t.text.length;
|
||||||
|
mixin (generateFixedLengthCases());
|
||||||
|
default:
|
||||||
|
return INVALID_TOKEN_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBreakToken(IdType t)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
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!"%":
|
||||||
|
case tok!"+=":
|
||||||
|
case tok!".":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int breakCost(IdType t)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case tok!"||":
|
||||||
|
case tok!"&&":
|
||||||
|
return 21;
|
||||||
|
case tok!"(":
|
||||||
|
case tok!",":
|
||||||
|
return 34;
|
||||||
|
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!"+=":
|
||||||
|
return 55;
|
||||||
|
case tok!".":
|
||||||
|
return 89;
|
||||||
|
default:
|
||||||
|
return 144;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
this(size_t[] breaks, const Token[] tokens, int depth,
|
||||||
|
const FormatterConfig* formatterConfig, int currentLineLength,
|
||||||
|
int indentLevel)
|
||||||
|
{
|
||||||
|
this.breaks = breaks;
|
||||||
|
this._depth = depth;
|
||||||
|
import std.algorithm : map, sum;
|
||||||
|
this._cost = breaks.map!(b => breakCost(tokens[b].type)).sum() + ((depth - 1) * 50);
|
||||||
|
int ll = currentLineLength;
|
||||||
|
size_t breakIndex = 0;
|
||||||
|
size_t i;
|
||||||
|
bool s = true;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
immutable size_t j = breakIndex < breaks.length ? breaks[breakIndex] : tokens.length;
|
||||||
|
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
|
||||||
|
writeln("ll = ", ll, " i = ", i, " j = ", j);
|
||||||
|
if (ll > formatterConfig.columnSoftLimit)
|
||||||
|
{
|
||||||
|
s = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i = j;
|
||||||
|
ll = (indentLevel + 1) * formatterConfig.indentSize;
|
||||||
|
writeln("ll2 = ", ll);
|
||||||
|
breakIndex++;
|
||||||
|
}
|
||||||
|
while (i + 1 < tokens.length);
|
||||||
|
this._solved = s;
|
||||||
|
writeln("breaks = ", breaks, " solved = ", this._solved);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cost() const @property { return _cost; }
|
||||||
|
int depth() const @property { return _depth; }
|
||||||
|
int solved() const @property { return _solved; }
|
||||||
|
|
||||||
|
int opCmp(ref const State other) const
|
||||||
|
{
|
||||||
|
if (other.cost < cost)
|
||||||
|
return -1;
|
||||||
|
return other.cost > cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref const State other) const
|
||||||
|
{
|
||||||
|
return other.breaks == breaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t[] breaks;
|
||||||
|
private:
|
||||||
|
int _cost;
|
||||||
|
int _depth;
|
||||||
|
bool _solved;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t[] chooseLineBreakTokens(const Token[] tokens,
|
||||||
|
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel)
|
||||||
|
{
|
||||||
|
import std.typecons : Tuple, tuple;
|
||||||
|
import std.container.rbtree : RedBlackTree;
|
||||||
|
import std.algorithm : map;
|
||||||
|
|
||||||
|
int depth = 0;
|
||||||
|
auto open = new RedBlackTree!State;
|
||||||
|
auto closed = new RedBlackTree!(State, "a.breaks < b.breaks");
|
||||||
|
open.insert(State(cast(size_t[])[], tokens, depth, formatterConfig,
|
||||||
|
currentLineLength, indentLevel));
|
||||||
|
while (!open.empty)
|
||||||
|
{
|
||||||
|
State current = open.front();
|
||||||
|
writeln("open = ", open[].map!(a => tuple(a.cost, a.solved)));
|
||||||
|
open.removeFront();
|
||||||
|
closed.insert(current);
|
||||||
|
if (current.solved)
|
||||||
|
return current.breaks;
|
||||||
|
foreach (next; validMoves(tokens, current, formatterConfig,
|
||||||
|
currentLineLength, indentLevel, depth))
|
||||||
|
{
|
||||||
|
auto r = closed.equalRange(next);
|
||||||
|
if (!r.empty)
|
||||||
|
{
|
||||||
|
if (current.cost > r.front.cost)
|
||||||
|
continue;
|
||||||
|
closed.remove(r);
|
||||||
|
}
|
||||||
|
open.insert(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln("No solution found");
|
||||||
|
return open.front().breaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
State[] validMoves(const Token[] tokens, ref const State current,
|
||||||
|
const FormatterConfig* formatterConfig, int currentLineLength,
|
||||||
|
int indentLevel, int depth)
|
||||||
|
{
|
||||||
|
import std.algorithm : sort, canFind;
|
||||||
|
import std.array:insertInPlace;
|
||||||
|
|
||||||
|
State[] states;
|
||||||
|
foreach (i, token; tokens)
|
||||||
|
{
|
||||||
|
if (!isBreakToken(token.type) || current.breaks.canFind(i))
|
||||||
|
continue;
|
||||||
|
size_t[] breaks;
|
||||||
|
breaks ~= current.breaks;
|
||||||
|
breaks ~= i;
|
||||||
|
sort(breaks);
|
||||||
|
states ~= State(breaks, tokens, depth + 1, formatterConfig,
|
||||||
|
currentLineLength, indentLevel);
|
||||||
|
}
|
||||||
|
writeln(states);
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
auto sourceCode = q{const Token[] tokens, ref const State current, const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel, int depth};
|
||||||
|
LexerConfig config;
|
||||||
|
config.stringBehavior = StringBehavior.source;
|
||||||
|
config.whitespaceBehavior = WhitespaceBehavior.skip;
|
||||||
|
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
||||||
|
auto tokens = byToken(cast(ubyte[]) sourceCode, config, &cache).array();
|
||||||
|
FormatterConfig formatterConfig;
|
||||||
|
writeln(chooseLineBreakTokens(tokens, &formatterConfig, 0, 0));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue