1 /* 2 Copyright (c) 2011-2014 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module dmodule; 30 31 import std.stdio; 32 import std.file; 33 import std.ascii; 34 import std.conv; 35 import std.array; 36 import std.path; 37 import std.datetime; 38 39 import lexer; 40 41 struct Pattern 42 { 43 string[] symbols; 44 uint position; 45 46 bool satisfies(string token) 47 { 48 if (symbols[position] == "*") 49 { 50 if (isIdentifier(token)) 51 { 52 position++; 53 if (position == symbols.length) 54 position = 0; 55 return true; 56 } 57 else 58 { 59 position = 0; 60 return false; 61 } 62 } 63 else if (symbols[position] == "*,") 64 { 65 if (isIdentifier(token)) 66 { 67 return true; 68 } 69 else if (token == ",") 70 { 71 return true; 72 } 73 else 74 { 75 position++; 76 if (position == symbols.length) 77 { 78 position = 0; 79 return false; 80 } 81 else 82 { 83 return satisfies(token); 84 } 85 } 86 } 87 else 88 { 89 string sym = symbols[position]; 90 if (token == sym) 91 { 92 position++; 93 if (position == symbols.length) 94 position = 0; 95 return true; 96 } 97 else 98 { 99 position = 0; 100 return false; 101 } 102 } 103 } 104 105 string toString() 106 { 107 return symbols.to!string; 108 } 109 } 110 111 struct FilterLevel 112 { 113 Pattern[] patterns; 114 bool positive = true; 115 116 void addPattern(string[] symbols) 117 { 118 patterns ~= Pattern(symbols, 0); 119 } 120 121 bool satisfies(string token) 122 { 123 bool res = false; 124 foreach(ref p; patterns) 125 if (positive) 126 res = res | p.satisfies(token); 127 else 128 res = res | (!p.satisfies(token)); 129 return res; 130 } 131 } 132 133 class Filter 134 { 135 Lexer lex; 136 FilterLevel[] levels; 137 string current; 138 139 this(string text) 140 { 141 lex = new Lexer(text); 142 lex.addDelimiters(); 143 } 144 145 FilterLevel* addLevel(bool positive = true) 146 { 147 FilterLevel flevel; 148 flevel.positive = positive; 149 levels ~= flevel; 150 return &levels[$-1]; 151 } 152 153 string getNext() 154 { 155 do 156 { 157 lex.readNext(); 158 string token = lex.current; 159 if (token == "") 160 return token; 161 else 162 { 163 bool res = false; 164 foreach(ref level; levels) 165 { 166 if (!level.satisfies(token)) 167 { 168 res = false; 169 break; 170 } 171 else 172 res = true; 173 } 174 if (res) 175 return token; 176 } 177 } 178 while(true); 179 } 180 181 void readNext() 182 { 183 current = getNext(); 184 } 185 } 186 187 bool isIdentifier(string s) 188 { 189 return (s.length && (isAlpha(s[0]) || s[0] == '_')); 190 } 191 192 string moduleToPath(string modulename, string ext) 193 { 194 string fname = modulename; 195 fname = replace(fname, ".", "/"); 196 fname = fname ~ ext; 197 return fname; 198 } 199 200 string pathToModule(string path) 201 { 202 string mdl = path; 203 mdl = replace(mdl, "/", "."); 204 return mdl; 205 } 206 207 final class DModule 208 { 209 private: 210 211 Filter filter; 212 string[string] imports; 213 214 public: 215 216 string filename; 217 string ext; 218 string packageName; 219 SysTime lastModified; 220 DModule[string] backdeps; 221 int[string] versionIds; 222 int[string] debugIds; 223 bool forceRebuild = false; 224 bool globalFile = false; 225 226 // TODO: simple debug condition 227 228 this(string filename, string ext) 229 { 230 this.filename = filename; 231 this.ext = ext; 232 233 auto text = readText(filename); 234 filter = new Filter(text); 235 auto l1 = filter.addLevel(); 236 l1.addPattern(["{"]); 237 l1.addPattern(["}"]); 238 l1.addPattern([";"]); 239 l1.addPattern(["else"]); 240 l1.addPattern(["version", "(", "*", ")"]); 241 l1.addPattern(["version", "=", "*"]); 242 l1.addPattern(["debug", "(", "*", ")"]); 243 l1.addPattern(["debug", "=", "*"]); 244 l1.addPattern(["import", "*,", ";"]); 245 l1.addPattern(["import", "*", ":"]); 246 l1.addPattern(["import", "*", "=", "*"]); 247 } 248 249 override string toString() 250 { 251 string output = lastModified.toISOExtString() ~ " " ~ to!string(cast(int)globalFile) ~ " "; 252 foreach(i,v; imports) 253 output ~= v ~ " "; 254 return output; 255 } 256 257 string[] importedModules() 258 { 259 return imports.keys; 260 } 261 262 string[] importedFiles() 263 { 264 return imports.values; 265 } 266 267 void addImportFile(string filename) 268 { 269 imports[pathToModule(stripExtension(filename))] = filename; 270 } 271 272 void addImport(string moduleName) 273 { 274 imports[moduleName] = moduleToPath(moduleName, ext); 275 } 276 277 bool buildDependencyList() 278 { 279 try 280 { 281 parseImports(); 282 } 283 catch(Exception) 284 { 285 writefln("%s: syntax error", filename); 286 return false; 287 } 288 return true; 289 } 290 291 private: 292 293 void parseImports() 294 { 295 string token = ""; 296 do 297 { 298 filter.readNext(); 299 token = filter.current; 300 if (token.length) 301 { 302 parseStatement(false); 303 } 304 } 305 while (token.length > 0); 306 } 307 308 void parseStatement(bool skip) 309 { 310 if (filter.current == "version" || filter.current == "debug") 311 { 312 if (!skip) 313 parseVersionStatement(); 314 } 315 else if (filter.current == "import") 316 { 317 if (!skip) 318 parseImportStatement(); 319 } 320 } 321 322 void parseVersionStatement() 323 { 324 string stat = filter.current; 325 filter.readNext(); // pop version/debug 326 327 if (filter.current == "(") 328 { 329 filter.readNext(); // pop ( 330 string id = filter.current; 331 332 filter.readNext(); // pop id 333 if (filter.current != ")") 334 throw new Exception("Syntax error"); 335 filter.readNext(); // pop ) 336 bool skip; 337 338 if (stat == "version") 339 skip = (id in versionIds) is null; 340 else if (stat == "debug") 341 skip = (id in debugIds) is null; 342 343 parseBlock(skip); 344 345 if (filter.current == "else") 346 { 347 parseBlock(!skip); 348 } 349 } 350 else if (filter.current == "=") 351 { 352 filter.readNext(); // pop = 353 string id = filter.current; 354 355 if (stat == "version") 356 versionIds[id] = 1; 357 else if (stat == "debug") 358 debugIds[id] = 1; 359 } 360 } 361 362 void parseImportStatement() 363 { 364 filter.readNext(); // pop import 365 string impName = filter.current; 366 filter.readNext(); // pop impName 367 368 // End of import list 369 if (filter.current == ";" || filter.current == ":") 370 { 371 addImport(impName); 372 return; 373 } 374 // Import list 375 else if (filter.current == ",") 376 { 377 addImport(impName); 378 parseImportStatement(); 379 } 380 // Renamed import 381 else if (filter.current == "=") 382 { 383 filter.readNext(); // pop = 384 addImport(filter.current); 385 } 386 } 387 388 void parseBlock(bool skip) 389 { 390 if (filter.current == "{") 391 { 392 while(filter.current != "}") 393 { 394 filter.readNext(); 395 parseStatement(skip); 396 if (filter.current == "") 397 throw new Exception("Syntax error"); 398 } 399 filter.readNext(); // pop } 400 } 401 else 402 { 403 parseStatement(skip); 404 } 405 } 406 } 407