1 //          Copyright Brian Schott 2015.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dfmt.tokens;
7 
8 import dparse.lexer;
9 
10 /// Length of an invalid token
11 enum int INVALID_TOKEN_LENGTH = -1;
12 
13 uint betweenParenLength(const Token[] tokens) pure @safe @nogc
14 in
15 {
16     assert(tokens[0].type == tok!"(");
17 }
18 body
19 {
20     uint length = 0;
21     size_t i = 1;
22     int depth = 1;
23     while (i < tokens.length && depth > 0)
24     {
25         if (tokens[i].type == tok!"(")
26             depth++;
27         else if (tokens[i].type == tok!")")
28             depth--;
29         length += tokenLength(tokens[i]);
30         i++;
31     }
32     return length;
33 }
34 
35 int tokenLength(ref const Token t) pure @safe @nogc
36 {
37     import std.algorithm : countUntil;
38 
39     int c;
40     switch (t.type)
41     {
42     case tok!"doubleLiteral":
43     case tok!"floatLiteral":
44     case tok!"idoubleLiteral":
45     case tok!"ifloatLiteral":
46     case tok!"intLiteral":
47     case tok!"longLiteral":
48     case tok!"realLiteral":
49     case tok!"irealLiteral":
50     case tok!"uintLiteral":
51     case tok!"ulongLiteral":
52     case tok!"characterLiteral":
53         return cast(int) t.text.length;
54     case tok!"identifier":
55     case tok!"stringLiteral":
56     case tok!"wstringLiteral":
57     case tok!"dstringLiteral":
58         // TODO: Unicode line breaks and old-Mac line endings
59         c = cast(int) t.text.countUntil('\n');
60         if (c == -1)
61             return cast(int) t.text.length;
62         else
63             return c;
64         mixin(generateFixedLengthCases());
65     default:
66         return INVALID_TOKEN_LENGTH;
67     }
68 }
69 
70 bool isBreakToken(IdType t) pure nothrow @safe @nogc
71 {
72     switch (t)
73     {
74     case tok!"||":
75     case tok!"&&":
76     case tok!"(":
77     case tok!"[":
78     case tok!",":
79     case tok!":":
80     case tok!";":
81     case tok!"^^":
82     case tok!"^=":
83     case tok!"^":
84     case tok!"~=":
85     case tok!"<<=":
86     case tok!"<<":
87     case tok!"<=":
88     case tok!"<>=":
89     case tok!"<>":
90     case tok!"<":
91     case tok!"==":
92     case tok!"=>":
93     case tok!"=":
94     case tok!">=":
95     case tok!">>=":
96     case tok!">>>=":
97     case tok!">>>":
98     case tok!">>":
99     case tok!">":
100     case tok!"|=":
101     case tok!"|":
102     case tok!"-=":
103     case tok!"!<=":
104     case tok!"!<>=":
105     case tok!"!<>":
106     case tok!"!<":
107     case tok!"!=":
108     case tok!"!>=":
109     case tok!"!>":
110     case tok!"?":
111     case tok!"/=":
112     case tok!"/":
113     case tok!"..":
114     case tok!"*=":
115     case tok!"*":
116     case tok!"&=":
117     case tok!"%=":
118     case tok!"%":
119     case tok!"+=":
120     case tok!".":
121     case tok!"~":
122     case tok!"+":
123     case tok!"-":
124         return true;
125     default:
126         return false;
127     }
128 }
129 
130 int breakCost(IdType p, IdType c) pure nothrow @safe @nogc
131 {
132     switch (c)
133     {
134     case tok!"||":
135     case tok!"&&":
136     case tok!",":
137     case tok!":":
138     case tok!"?":
139         return 0;
140     case tok!"(":
141         return 60;
142     case tok!"[":
143         return 300;
144     case tok!";":
145     case tok!"^^":
146     case tok!"^=":
147     case tok!"^":
148     case tok!"~=":
149     case tok!"<<=":
150     case tok!"<<":
151     case tok!"<=":
152     case tok!"<>=":
153     case tok!"<>":
154     case tok!"<":
155     case tok!"==":
156     case tok!"=>":
157     case tok!"=":
158     case tok!">=":
159     case tok!">>=":
160     case tok!">>>=":
161     case tok!">>>":
162     case tok!">>":
163     case tok!">":
164     case tok!"|=":
165     case tok!"|":
166     case tok!"-=":
167     case tok!"!<=":
168     case tok!"!<>=":
169     case tok!"!<>":
170     case tok!"!<":
171     case tok!"!=":
172     case tok!"!>=":
173     case tok!"!>":
174     case tok!"/=":
175     case tok!"/":
176     case tok!"..":
177     case tok!"*=":
178     case tok!"*":
179     case tok!"&=":
180     case tok!"%=":
181     case tok!"%":
182     case tok!"+":
183     case tok!"-":
184     case tok!"~":
185     case tok!"+=":
186         return 200;
187     case tok!".":
188         return p == tok!")" ? 0 : 300;
189     default:
190         return 1000;
191     }
192 }
193 
194 pure nothrow @safe @nogc unittest
195 {
196     foreach (ubyte u; 0 .. ubyte.max)
197         if (isBreakToken(u))
198             assert(breakCost(tok!".", u) != 1000);
199 }
200 
201 private string generateFixedLengthCases()
202 {
203     import std.algorithm : map;
204     import std..string : format;
205     import std.array : join;
206 
207     assert(__ctfe);
208 
209     string[] spacedOperatorTokens = [
210         ",", "..", "...", "/", "/=", "!", "!<", "!<=", "!<>", "!<>=", "!=",
211         "!>", "!>=", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", "-",
212         "-=", ":", ";", "<", "<<", "<<=", "<=", "<>", "<>=", "=", "==", "=>",
213         ">", ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "^", "^=", "^^",
214         "^^=", "|", "|=", "||", "~", "~="
215     ];
216     immutable spacedOperatorTokenCases = spacedOperatorTokens.map!(
217             a => format(`case tok!"%s": return %d + 1;`, a, a.length)).join("\n\t");
218 
219     string[] identifierTokens = [
220         "abstract", "alias", "align", "asm", "assert", "auto", "bool",
221         "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", "char", "class",
222         "const", "continue", "creal", "dchar", "debug", "default", "delegate", "delete", "deprecated",
223         "do", "double", "else", "enum", "export", "extern", "false", "final", "finally", "float",
224         "for", "foreach", "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable",
225         "import", "in", "inout", "int", "interface", "invariant", "ireal", "is",
226         "lazy", "long", "macro", "mixin", "module", "new", "nothrow", "null", "out", "override",
227         "package", "pragma", "private", "protected", "public", "pure", "real", "ref", "return", "scope",
228         "shared", "short", "static", "struct", "super", "switch", "synchronized", "template", "this",
229         "throw", "true", "try", "typedef", "typeid", "typeof", "ubyte", "ucent", "uint", "ulong",
230         "union", "unittest", "ushort", "version", "void", "volatile", "wchar",
231         "while", "with", "__DATE__", "__EOF__", "__FILE__",
232         "__FUNCTION__", "__gshared", "__LINE__", "__MODULE__", "__parameters",
233         "__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__",
234         "__traits", "__vector", "__VENDOR__", "__VERSION__", "$", "++", "--",
235         ".", "[", "]", "(", ")", "{", "}"
236     ];
237     immutable identifierTokenCases = identifierTokens.map!(
238             a => format(`case tok!"%s": return %d;`, a, a.length)).join("\n\t");
239     return spacedOperatorTokenCases ~ identifierTokenCases;
240 }