1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity average) 4 module cmd.Statistics; 5 6 import cmd.Command, 7 cmd.ASTStats; 8 import dil.lexer.Lexer, 9 dil.lexer.Token; 10 import dil.parser.Parser; 11 import dil.ast.NodesEnum; 12 import dil.Compilation; 13 import dil.SourceText; 14 import common; 15 16 /// The statistics comman. 17 class StatsCommand : Command 18 { 19 cstring[] filePaths; /// Module file paths. 20 bool printTokensTable; /// Whether to print the tokens table. 21 bool printNodesTable; /// Whether to print the nodes table. 22 CompilationContext cc; /// The context. 23 24 /// Execute the command. 25 override void run() 26 { 27 Statistics[] stats; 28 foreach (filePath; filePaths) 29 stats ~= getStatistics(cc, filePath, printTokensTable, printNodesTable); 30 31 auto total = Statistics(printTokensTable, printNodesTable); 32 33 foreach (i, ref stat; stats) 34 { 35 total += stat; 36 Stdout.formatln( 37 "----\n" 38 "File: {}\n" 39 "Whitespace character count: {}\n" 40 "Whitespace token count: {}\n" 41 "Keyword count: {}\n" 42 "Identifier count: {}\n" 43 "Number count: {}\n" 44 "Comment count: {}\n" 45 "All tokens count: {}\n" 46 "Lines of code: {}", 47 filePaths[i], 48 stat.whitespaceCount, 49 stat.wsTokenCount, 50 stat.keywordCount, 51 stat.identCount, 52 stat.numberCount, 53 stat.commentCount, 54 stat.tokenCount, 55 stat.linesOfCode 56 ); 57 } 58 59 if (filePaths.length > 1) 60 { 61 Stdout.formatln( 62 "--------------------------------------------------------------------------------\n" 63 "Total of {} files:\n" 64 "Whitespace character count: {}\n" 65 "Whitespace token count: {}\n" 66 "Keyword count: {}\n" 67 "Identifier count: {}\n" 68 "Number count: {}\n" 69 "Comment count: {}\n" 70 "All tokens count: {}\n" 71 "Lines of code: {}", 72 filePaths.length, 73 total.whitespaceCount, 74 total.wsTokenCount, 75 total.keywordCount, 76 total.identCount, 77 total.numberCount, 78 total.commentCount, 79 total.tokenCount, 80 total.linesOfCode 81 ); 82 } 83 84 if (printTokensTable) 85 { 86 Stdout("Table of tokens:").newline; 87 Stdout.formatln(" {,10} | {}", "Count", "Token kind"); 88 Stdout("-----------------------------").newline; 89 foreach (i, count; total.tokensTable) 90 Stdout.formatln(" {,10} | {}", count, (cast(TOK)i).toString); 91 Stdout("// End of tokens table.").newline; 92 } 93 94 if (printNodesTable) 95 { 96 Stdout("Table of nodes:").newline; 97 Stdout.formatln(" {,10} | {}", "Count", "Node kind"); 98 Stdout("-----------------------------").newline; 99 foreach (i, count; total.nodesTable) 100 Stdout.formatln(" {,10} | {}", count, NodeClassNames[i]); 101 Stdout("// End of nodes table.").newline; 102 } 103 } 104 } 105 106 /// A group of statistics variables. 107 struct Statistics 108 { 109 uint whitespaceCount; /// Counter for whitespace characters. 110 uint wsTokenCount; /// Counter for all whitespace tokens. 111 uint keywordCount; /// Counter for keywords. 112 uint identCount; /// Counter for identifiers. 113 uint numberCount; /// Counter for number literals. 114 uint commentCount; /// Counter for comments. 115 uint tokenCount; /// Counter for all tokens produced by the Lexer. 116 uint linesOfCode; /// Number of lines. 117 uint[] tokensTable; /// Table of counters for all token kinds. 118 uint[] nodesTable; /// Table of counters for all node kinds. 119 120 static Statistics opCall(bool allocateTokensTable, bool allocateNodesTable = false) 121 { 122 Statistics s; 123 if (allocateTokensTable) 124 s.tokensTable = new uint[TOK.MAX]; 125 if (allocateNodesTable) 126 s.nodesTable = new uint[NodeClassNames.length]; 127 return s; 128 } 129 130 void opAddAssign(Statistics s) 131 { 132 this.whitespaceCount += s.whitespaceCount; 133 this.wsTokenCount += s.wsTokenCount; 134 this.keywordCount += s.keywordCount; 135 this.identCount += s.identCount; 136 this.numberCount += s.numberCount; 137 this.commentCount += s.commentCount; 138 this.tokenCount += s.tokenCount; 139 this.linesOfCode += s.linesOfCode; 140 foreach (i, count; s.tokensTable) 141 this.tokensTable[i] += count; 142 foreach (i, count; s.nodesTable) 143 this.nodesTable[i] += count; 144 } 145 } 146 147 /// Returns the statistics for a D source file. 148 Statistics getStatistics(CompilationContext cc, cstring filePath, 149 bool printTokensTable, bool printNodesTable) 150 { 151 // Create a new record. 152 auto stats = Statistics(printTokensTable); 153 154 auto sourceText = new SourceText(filePath, true); 155 Parser parser; 156 Lexer lx; 157 if (printNodesTable) 158 { 159 parser = new Parser(sourceText, cc.tables.lxtables); 160 auto rootNode = parser.start(); 161 // Count nodes. 162 stats.nodesTable = (new ASTStats).count(rootNode); 163 lx = parser.lexer; 164 } 165 else 166 { 167 lx = new Lexer(sourceText, cc.tables.lxtables); 168 lx.scanAll(); 169 } 170 171 // Count tokens. 172 // Lexer creates HEAD + Newline, which are not in the source text. 173 // No token left behind! 174 stats.tokenCount = 2; 175 stats.linesOfCode = lx.lineNum; 176 if (printTokensTable) 177 { 178 stats.tokensTable[TOK.HEAD] = 1; 179 stats.tokensTable[TOK.Newline] = 1; 180 } 181 182 // Traverse linked list. 183 foreach (token; lx.tokenList[0..$-1]) 184 { 185 stats.tokenCount += 1; 186 187 if (printTokensTable) 188 stats.tokensTable[token.kind] += 1; 189 190 // Count whitespace characters 191 if (token.ws !is null) 192 stats.whitespaceCount += token.start - token.ws; 193 194 switch (token.kind) 195 { 196 case TOK.Identifier: 197 stats.identCount++; 198 break; 199 case TOK.Comment: 200 stats.commentCount++; 201 break; 202 case TOK.Int32, TOK.Int64, TOK.UInt32, TOK.UInt64, 203 TOK.Float32, TOK.Float64, TOK.Float80, 204 TOK.IFloat32, TOK.IFloat64, TOK.IFloat80: 205 stats.numberCount++; 206 break; 207 case TOK.Newline: 208 break; 209 default: 210 if (token.isKeyword) 211 stats.keywordCount++; 212 else if (token.isWhitespace) 213 stats.wsTokenCount++; 214 } 215 } 216 return stats; 217 }