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 do 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 return 0; 139 case tok!"(": 140 return 60; 141 case tok!"[": 142 return 300; 143 case tok!";": 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 return 200; 186 case tok!":": 187 // colon could be after a label or an import, where it should normally wrap like before 188 // for everything else (associative arrays) try not breaking around colons 189 return p == tok!"identifier" ? 0 : 300; 190 case tok!".": 191 return p == tok!")" ? 0 : 300; 192 default: 193 return 1000; 194 } 195 } 196 197 pure nothrow @safe @nogc unittest 198 { 199 foreach (ubyte u; 0 .. ubyte.max) 200 if (isBreakToken(u)) 201 assert(breakCost(tok!".", u) != 1000); 202 } 203 204 private string generateFixedLengthCases() 205 { 206 import std.algorithm : map; 207 import std..string : format; 208 import std.array : join; 209 210 assert(__ctfe); 211 212 string[] spacedOperatorTokens = [ 213 ",", "..", "...", "/", "/=", "!", "!<", "!<=", "!<>", "!<>=", "!=", 214 "!>", "!>=", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", "-", 215 "-=", ":", ";", "<", "<<", "<<=", "<=", "<>", "<>=", "=", "==", "=>", 216 ">", ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "^", "^=", "^^", 217 "^^=", "|", "|=", "||", "~", "~=" 218 ]; 219 immutable spacedOperatorTokenCases = spacedOperatorTokens.map!( 220 a => format(`case tok!"%s": return %d + 1;`, a, a.length)).join("\n\t"); 221 222 string[] identifierTokens = [ 223 "abstract", "alias", "align", "asm", "assert", "auto", "bool", 224 "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", "char", "class", 225 "const", "continue", "creal", "dchar", "debug", "default", "delegate", "delete", "deprecated", 226 "do", "double", "else", "enum", "export", "extern", "false", "final", "finally", "float", 227 "for", "foreach", "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable", 228 "import", "in", "inout", "int", "interface", "invariant", "ireal", "is", 229 "lazy", "long", "macro", "mixin", "module", "new", "nothrow", "null", "out", "override", 230 "package", "pragma", "private", "protected", "public", "pure", "real", "ref", "return", "scope", 231 "shared", "short", "static", "struct", "super", "switch", "synchronized", "template", "this", 232 "throw", "true", "try", "typedef", "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", 233 "union", "unittest", "ushort", "version", "void", "wchar", 234 "while", "with", "__DATE__", "__EOF__", "__FILE__", 235 "__FUNCTION__", "__gshared", "__LINE__", "__MODULE__", "__parameters", 236 "__PRETTY_FUNCTION__", "__TIME__", "__TIMESTAMP__", 237 "__traits", "__vector", "__VENDOR__", "__VERSION__", "$", "++", "--", 238 ".", "[", "]", "(", ")", "{", "}" 239 ]; 240 immutable identifierTokenCases = identifierTokens.map!( 241 a => format(`case tok!"%s": return %d;`, a, a.length)).join("\n\t"); 242 return spacedOperatorTokenCases ~ identifierTokenCases; 243 }