Reid Spencer | 2594c9a | 2004-08-13 20:21:22 +0000 | [diff] [blame^] | 1 | //===- ConfigData.cpp - Configuration Data Mgmt -----------------*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file was developed by Reid Spencer and is distributed under the |
| 6 | // University of Illinois Open Source License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file implements the parsing of configuration files for the LLVM Compiler |
| 11 | // Driver (llvmc). |
| 12 | // |
| 13 | //===------------------------------------------------------------------------=== |
| 14 | |
| 15 | #include "ConfigData.h" |
| 16 | #include "CompilerDriver.h" |
| 17 | #include "Support/StringExtras.h" |
| 18 | #include <iostream> |
| 19 | |
| 20 | using namespace llvm; |
| 21 | |
| 22 | namespace { |
| 23 | |
| 24 | // This array of strings provides the input for ".ll" files (LLVM Assembly) |
| 25 | // to the configuration file parser. This data is just "built-in" to |
| 26 | // llvmc so it doesn't have to be read from a configuration file. |
| 27 | static const char* LL_Data[] = { |
| 28 | "lang.name=LLVM Assembly", |
| 29 | "lang.translator.preprocesses=false", |
| 30 | "lang.translator.optimizes=No", |
| 31 | "lang.translator.groks_dash_O=No", |
| 32 | "lang.preprocessor.needed=0", |
| 33 | "preprocessor.prog=", |
| 34 | "preprocessor.args=", |
| 35 | "translator.prog=llvm-as", |
| 36 | "translator.args=@in@ -o @out@", |
| 37 | "optimizer.prog=opt", |
| 38 | "optimizer.args=@in@ -o @out@", |
| 39 | "assembler.prog=llc", |
| 40 | "assembler.args=@in@ -o @out@", |
| 41 | "linker.prog=llvm-link", |
| 42 | "linker.args=@in@ -o @out@" |
| 43 | }; |
| 44 | |
| 45 | // This array of strings provides the input for ".st" files (Stacker). |
| 46 | static const char* ST_Data[] = { |
| 47 | "lang.name=Stacker", |
| 48 | "lang.translator.preprocesses=false", |
| 49 | "lang.translator.optimizes=true", |
| 50 | "lang.translator.groks_dash_O=0", |
| 51 | "lang.preprocessor.needed=0", |
| 52 | "preprocessor.prog=cp", |
| 53 | "preprocessor.args=@in@ @out@", |
| 54 | "translator.prog=stkrc", |
| 55 | "translator.args=@in@ -o @out@ -S 2048", |
| 56 | "optimizer.prog=opt", |
| 57 | "optimizer.args=@in@ -o @out@", |
| 58 | "assembler.prog=llc", |
| 59 | "assembler.args=@in@ -o @out@", |
| 60 | "linker.prog=llvm-link", |
| 61 | "linker.args=@in@ -o @out@" |
| 62 | }; |
| 63 | |
| 64 | class InputProvider { |
| 65 | public: |
| 66 | virtual bool getLine(std::string& line) = 0; |
| 67 | virtual void error(const std::string& msg) = 0; |
| 68 | virtual bool errorOccurred() = 0; |
| 69 | }; |
| 70 | |
| 71 | class StaticInputProvider : public InputProvider { |
| 72 | public: |
| 73 | StaticInputProvider(const char *data[], size_t count, |
| 74 | const std::string& nam) { |
| 75 | TheData = data; |
| 76 | limit = count; |
| 77 | where = 0; |
| 78 | name = nam; |
| 79 | errCount = 0; |
| 80 | } |
| 81 | virtual ~StaticInputProvider() {} |
| 82 | virtual bool getLine(std::string& line) { |
| 83 | if ( where >= limit ) return false; |
| 84 | line = TheData[where++]; |
| 85 | return true; |
| 86 | } |
| 87 | |
| 88 | virtual void error(const std::string& msg) { |
| 89 | std::cerr << name << ":" << where << ": Error: " << msg << "\n"; |
| 90 | errCount++; |
| 91 | } |
| 92 | |
| 93 | virtual bool errorOccurred() { return errCount > 0; }; |
| 94 | |
| 95 | private: |
| 96 | const char**TheData; |
| 97 | size_t limit; |
| 98 | size_t where; |
| 99 | std::string name; |
| 100 | size_t errCount; |
| 101 | }; |
| 102 | |
| 103 | inline bool recognize(const char*& p, const char*token) { |
| 104 | while (*p == *token && *token != '\0') |
| 105 | ++token, p++; |
| 106 | return *token == '\0' && ((*p == '\0') || ( *p == '.' ) || (*p == '=')); |
| 107 | } |
| 108 | |
| 109 | inline bool getBoolean(const std::string& value) { |
| 110 | switch (value[0]) { |
| 111 | case 't': |
| 112 | case 'T': |
| 113 | case '1': |
| 114 | case 'y': |
| 115 | case 'Y': |
| 116 | return true; |
| 117 | default : |
| 118 | return false; |
| 119 | } |
| 120 | return false; |
| 121 | } |
| 122 | |
| 123 | inline void skipWhitespace( size_t& pos, const std::string& line ) { |
| 124 | while (pos < line.size() && ( |
| 125 | line[pos] == ' ' || // Space |
| 126 | line[pos] == '\t' || // Horizontal Tab |
| 127 | line[pos] == '\n' || // New Line |
| 128 | line[pos] == '\v' || // Vertical Tab |
| 129 | line[pos] == '\f' || // Form Feed |
| 130 | line[pos] == '\r') // Carriate Return |
| 131 | ) |
| 132 | pos++; |
| 133 | } |
| 134 | |
| 135 | inline void parseArgs(CompilerDriver::Action& pat, |
| 136 | const std::string& value, |
| 137 | InputProvider& provider ) |
| 138 | { |
| 139 | const char* p = value.c_str(); |
| 140 | const char* argStart = p; |
| 141 | while (*p != '\0') { |
| 142 | switch (*p) { |
| 143 | case ' ': |
| 144 | if (argStart != p) |
| 145 | pat.args.push_back(std::string(argStart, p-argStart)); |
| 146 | argStart = ++p; |
| 147 | break; |
| 148 | case '@' : |
| 149 | { |
| 150 | if (argStart != p) |
| 151 | pat.args.push_back(std::string(argStart,p-argStart)); |
| 152 | const char* token = ++p; |
| 153 | while (*p != '@' && *p != 0) |
| 154 | p++; |
| 155 | if ( *p != '@' ) { |
| 156 | provider.error("Unterminated substitution token"); |
| 157 | return; |
| 158 | } else { |
| 159 | p++; |
| 160 | bool legal = false; |
| 161 | switch (token[0]) { |
| 162 | case 'i': |
| 163 | if (token[1] == 'n' && token[2] == '@' ) { |
| 164 | pat.inputAt = pat.args.size(); |
| 165 | pat.args.push_back("in"); |
| 166 | legal = true; |
| 167 | argStart = p; |
| 168 | } |
| 169 | break; |
| 170 | case 'o': |
| 171 | if (token[1] == 'u' && token[2] == 't' && token[3] == '@') { |
| 172 | pat.outputAt = pat.args.size(); |
| 173 | pat.args.push_back("out"); |
| 174 | legal = true; |
| 175 | argStart = p; |
| 176 | } |
| 177 | break; |
| 178 | default: |
| 179 | break; |
| 180 | } |
| 181 | if (!legal) { |
| 182 | provider.error("Invalid substitution token"); |
| 183 | return; |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | break; |
| 188 | default : |
| 189 | p++; |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | CompilerDriver::ConfigData* |
| 196 | ParseConfigData(InputProvider& provider) { |
| 197 | std::string line; |
| 198 | CompilerDriver::ConfigData data; |
| 199 | while ( provider.getLine(line) ) { |
| 200 | // Check line length first |
| 201 | size_t lineLen = line.size(); |
| 202 | if (lineLen > 4096) |
| 203 | provider.error("length of input line (" + utostr(lineLen) + |
| 204 | ") is too long"); |
| 205 | |
| 206 | // First, skip whitespace |
| 207 | size_t stPos = 0; |
| 208 | skipWhitespace(stPos, line); |
| 209 | |
| 210 | // See if there's a hash mark. It and everything after it is |
| 211 | // ignored so lets delete that now. |
| 212 | size_t hashPos = line.find('#'); |
| 213 | if (hashPos != std::string::npos) |
| 214 | line.erase(hashPos); |
| 215 | |
| 216 | // Make sure we have something left to parse |
| 217 | if (line.size() == 0) |
| 218 | continue; // ignore full-line comment or whitespace line |
| 219 | |
| 220 | // Find the equals sign |
| 221 | size_t eqPos = line.find('='); |
| 222 | if (eqPos == std::string::npos) |
| 223 | provider.error("Configuration directive is missing an ="); |
| 224 | |
| 225 | // extract the item name |
| 226 | std::string name(line, stPos, eqPos-stPos); |
| 227 | |
| 228 | // directives without names are illegal |
| 229 | if (name.empty()) |
| 230 | provider.error("Configuration directive name is empty"); |
| 231 | |
| 232 | // Skip whitespace in the value |
| 233 | size_t valPos = eqPos + 1; |
| 234 | skipWhitespace(valPos, line); |
| 235 | |
| 236 | // Skip white space at end of value |
| 237 | size_t endPos = line.length() - 1; |
| 238 | while (line[endPos] == ' ') |
| 239 | endPos--; |
| 240 | |
| 241 | // extract the item value |
| 242 | std::string value(line, valPos, endPos-valPos+1); |
| 243 | |
| 244 | // Get the configuration item as a char pointer |
| 245 | const char*p = name.c_str(); |
| 246 | |
| 247 | // Indicate we haven't found an invalid item yet. |
| 248 | bool invalidItem = false; |
| 249 | |
| 250 | // Parse the contents by examining first character and |
| 251 | // using the recognize function strategically |
| 252 | switch (*p++) { |
| 253 | case 'l' : |
| 254 | // could it be "lang." |
| 255 | if (*p == 'a') { // "lang." ? |
| 256 | if (recognize(p,"ang")) { |
| 257 | p++; |
| 258 | switch (*p++) { |
| 259 | case 'n': |
| 260 | if (recognize(p,"ame")) |
| 261 | data.langName = value; |
| 262 | else |
| 263 | invalidItem = true; |
| 264 | break; |
| 265 | case 't': |
| 266 | if (recognize(p,"ranslator")) { |
| 267 | p++; |
| 268 | if (recognize(p,"preprocesses")) |
| 269 | data.TranslatorPreprocesses = getBoolean(value); |
| 270 | else if (recognize(p, "optimizes")) |
| 271 | data.TranslatorOptimizes = getBoolean(value); |
| 272 | else if (recognize(p, "groks_dash_O")) |
| 273 | data.TranslatorGroksDashO = getBoolean(value); |
| 274 | else |
| 275 | invalidItem = true; |
| 276 | } |
| 277 | else |
| 278 | invalidItem = true; |
| 279 | break; |
| 280 | case 'p': |
| 281 | if (recognize(p,"reprocessor")) { |
| 282 | p++; |
| 283 | if (recognize(p,"needed")) { |
| 284 | data.PreprocessorNeeded = getBoolean(value); |
| 285 | } else |
| 286 | invalidItem = true; |
| 287 | } |
| 288 | else |
| 289 | invalidItem = true; |
| 290 | break; |
| 291 | |
| 292 | default: |
| 293 | invalidItem = true; |
| 294 | break; |
| 295 | } |
| 296 | } |
| 297 | } else if (*p == 'i') { // "linker." ? |
| 298 | if (recognize(p,"inker")) { |
| 299 | p++; |
| 300 | if (recognize(p,"prog")) |
| 301 | data.Linker.program = value; |
| 302 | else if (recognize(p,"args")) |
| 303 | parseArgs(data.Linker,value,provider); |
| 304 | else |
| 305 | invalidItem = true; |
| 306 | } |
| 307 | else |
| 308 | invalidItem = true; |
| 309 | } else { |
| 310 | invalidItem = true; |
| 311 | } |
| 312 | break; |
| 313 | |
| 314 | case 'p' : |
| 315 | if (*p == 'r') { // "preprocessor." ? |
| 316 | if (recognize(p, "reprocessor")) { |
| 317 | p++; |
| 318 | if (recognize(p,"prog")) |
| 319 | data.PreProcessor.program = value; |
| 320 | else if (recognize(p,"args")) |
| 321 | parseArgs(data.PreProcessor,value,provider); |
| 322 | else |
| 323 | invalidItem = true; |
| 324 | } else |
| 325 | invalidItem = true; |
| 326 | } else { |
| 327 | invalidItem = true; |
| 328 | } |
| 329 | break; |
| 330 | |
| 331 | case 't' : |
| 332 | if (*p == 'r') { // "translator." ? |
| 333 | if (recognize(p, "ranslator")) { |
| 334 | p++; |
| 335 | if (recognize(p,"prog")) |
| 336 | data.Translator.program = value; |
| 337 | else if (recognize(p,"args")) |
| 338 | parseArgs(data.Translator,value,provider); |
| 339 | else |
| 340 | invalidItem = true; |
| 341 | } else |
| 342 | invalidItem = true; |
| 343 | } else { |
| 344 | invalidItem = true; |
| 345 | } |
| 346 | break; |
| 347 | |
| 348 | case 'o' : |
| 349 | if (*p == 'p') { // "optimizer." ? |
| 350 | if (recognize(p, "ptimizer")) { |
| 351 | p++; |
| 352 | if (recognize(p,"prog")) |
| 353 | data.Optimizer.program = value; |
| 354 | else if (recognize(p,"args")) |
| 355 | parseArgs(data.Optimizer,value,provider); |
| 356 | else |
| 357 | invalidItem = true; |
| 358 | } else |
| 359 | invalidItem = true; |
| 360 | } else { |
| 361 | invalidItem = true; |
| 362 | } |
| 363 | break; |
| 364 | case 'a' : |
| 365 | if (*p == 's') { // "assembler." ? |
| 366 | if (recognize(p, "ssembler")) { |
| 367 | p++; |
| 368 | if (recognize(p,"prog")) |
| 369 | data.Assembler.program = value; |
| 370 | else if (recognize(p,"args")) |
| 371 | parseArgs(data.Assembler,value,provider); |
| 372 | else |
| 373 | invalidItem = true; |
| 374 | } else |
| 375 | invalidItem = true; |
| 376 | } else { |
| 377 | invalidItem = true; |
| 378 | } |
| 379 | break; |
| 380 | } |
| 381 | if (invalidItem) |
| 382 | provider.error("Invalid configuration item: " + line.substr(stPos, eqPos-stPos)); |
| 383 | } |
| 384 | return new CompilerDriver::ConfigData(data); |
| 385 | } |
| 386 | |
| 387 | CompilerDriver::ConfigData* |
| 388 | ReadConfigData(const std::string& ftype) { |
| 389 | if ( ftype == "ll" ) { |
| 390 | StaticInputProvider sip(LL_Data, sizeof(LL_Data)/sizeof(LL_Data[0]), |
| 391 | "LLVM Assembly (internal)"); |
| 392 | return ParseConfigData(sip); |
| 393 | } else if (ftype == "st") { |
| 394 | StaticInputProvider sip(ST_Data, sizeof(ST_Data)/sizeof(ST_Data[0]), |
| 395 | "Stacker (internal)"); |
| 396 | return ParseConfigData(sip); |
| 397 | } |
| 398 | return 0; |
| 399 | } |
| 400 | |
| 401 | } |
| 402 | |
| 403 | LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() |
| 404 | : Configurations() |
| 405 | , configDir() |
| 406 | { |
| 407 | Configurations.clear(); |
| 408 | } |
| 409 | |
| 410 | LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider() |
| 411 | { |
| 412 | ConfigDataMap::iterator cIt = Configurations.begin(); |
| 413 | while (cIt != Configurations.end()) { |
| 414 | CompilerDriver::ConfigData* cd = cIt->second; |
| 415 | ++cIt; |
| 416 | delete cd; |
| 417 | } |
| 418 | Configurations.clear(); |
| 419 | } |
| 420 | |
| 421 | CompilerDriver::ConfigData* |
| 422 | LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) { |
| 423 | CompilerDriver::ConfigData* result = 0; |
| 424 | if (!Configurations.empty()) { |
| 425 | ConfigDataMap::iterator cIt = Configurations.find(filetype); |
| 426 | if ( cIt != Configurations.end() ) { |
| 427 | // We found one in the case, return it. |
| 428 | result = cIt->second; |
| 429 | } |
| 430 | } |
| 431 | if (result == 0) { |
| 432 | // The configuration data doesn't exist, we have to go read it. |
| 433 | result = ReadConfigData(filetype); |
| 434 | // If we got one, cache it |
| 435 | if ( result != 0 ) |
| 436 | Configurations.insert(std::make_pair(filetype,result)); |
| 437 | } |
| 438 | return result; // Might return 0 |
| 439 | } |
| 440 | |
| 441 | // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab |