//===- ConfigData.cpp - Configuration Data Mgmt -----------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file was developed by Reid Spencer and is distributed under the // University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the parsing of configuration files for the LLVM Compiler // Driver (llvmc). // //===------------------------------------------------------------------------=== #include "ConfigData.h" #include "CompilerDriver.h" #include "Support/StringExtras.h" #include using namespace llvm; namespace { // This array of strings provides the input for ".ll" files (LLVM Assembly) // to the configuration file parser. This data is just "built-in" to // llvmc so it doesn't have to be read from a configuration file. static const char* LL_Data[] = { "lang.name=LLVM Assembly", "lang.translator.preprocesses=false", "lang.translator.optimizes=No", "lang.translator.groks_dash_O=No", "lang.preprocessor.needed=0", "preprocessor.prog=", "preprocessor.args=", "translator.prog=llvm-as", "translator.args=@in@ -o @out@", "optimizer.prog=opt", "optimizer.args=@in@ -o @out@", "assembler.prog=llc", "assembler.args=@in@ -o @out@", "linker.prog=llvm-link", "linker.args=@in@ -o @out@" }; // This array of strings provides the input for ".st" files (Stacker). static const char* ST_Data[] = { "lang.name=Stacker", "lang.translator.preprocesses=false", "lang.translator.optimizes=true", "lang.translator.groks_dash_O=0", "lang.preprocessor.needed=0", "preprocessor.prog=cp", "preprocessor.args=@in@ @out@", "translator.prog=stkrc", "translator.args=@in@ -o @out@ -S 2048", "optimizer.prog=opt", "optimizer.args=@in@ -o @out@", "assembler.prog=llc", "assembler.args=@in@ -o @out@", "linker.prog=llvm-link", "linker.args=@in@ -o @out@" }; class InputProvider { public: virtual bool getLine(std::string& line) = 0; virtual void error(const std::string& msg) = 0; virtual bool errorOccurred() = 0; }; class StaticInputProvider : public InputProvider { public: StaticInputProvider(const char *data[], size_t count, const std::string& nam) { TheData = data; limit = count; where = 0; name = nam; errCount = 0; } virtual ~StaticInputProvider() {} virtual bool getLine(std::string& line) { if ( where >= limit ) return false; line = TheData[where++]; return true; } virtual void error(const std::string& msg) { std::cerr << name << ":" << where << ": Error: " << msg << "\n"; errCount++; } virtual bool errorOccurred() { return errCount > 0; }; private: const char**TheData; size_t limit; size_t where; std::string name; size_t errCount; }; inline bool recognize(const char*& p, const char*token) { while (*p == *token && *token != '\0') ++token, p++; return *token == '\0' && ((*p == '\0') || ( *p == '.' ) || (*p == '=')); } inline bool getBoolean(const std::string& value) { switch (value[0]) { case 't': case 'T': case '1': case 'y': case 'Y': return true; default : return false; } return false; } inline void skipWhitespace( size_t& pos, const std::string& line ) { while (pos < line.size() && ( line[pos] == ' ' || // Space line[pos] == '\t' || // Horizontal Tab line[pos] == '\n' || // New Line line[pos] == '\v' || // Vertical Tab line[pos] == '\f' || // Form Feed line[pos] == '\r') // Carriate Return ) pos++; } inline void parseArgs(CompilerDriver::Action& pat, const std::string& value, InputProvider& provider ) { const char* p = value.c_str(); const char* argStart = p; while (*p != '\0') { switch (*p) { case ' ': if (argStart != p) pat.args.push_back(std::string(argStart, p-argStart)); argStart = ++p; break; case '@' : { if (argStart != p) pat.args.push_back(std::string(argStart,p-argStart)); const char* token = ++p; while (*p != '@' && *p != 0) p++; if ( *p != '@' ) { provider.error("Unterminated substitution token"); return; } else { p++; bool legal = false; switch (token[0]) { case 'i': if (token[1] == 'n' && token[2] == '@' ) { pat.inputAt = pat.args.size(); pat.args.push_back("in"); legal = true; argStart = p; } break; case 'o': if (token[1] == 'u' && token[2] == 't' && token[3] == '@') { pat.outputAt = pat.args.size(); pat.args.push_back("out"); legal = true; argStart = p; } break; default: break; } if (!legal) { provider.error("Invalid substitution token"); return; } } } break; default : p++; break; } } } CompilerDriver::ConfigData* ParseConfigData(InputProvider& provider) { std::string line; CompilerDriver::ConfigData data; while ( provider.getLine(line) ) { // Check line length first size_t lineLen = line.size(); if (lineLen > 4096) provider.error("length of input line (" + utostr(lineLen) + ") is too long"); // First, skip whitespace size_t stPos = 0; skipWhitespace(stPos, line); // See if there's a hash mark. It and everything after it is // ignored so lets delete that now. size_t hashPos = line.find('#'); if (hashPos != std::string::npos) line.erase(hashPos); // Make sure we have something left to parse if (line.size() == 0) continue; // ignore full-line comment or whitespace line // Find the equals sign size_t eqPos = line.find('='); if (eqPos == std::string::npos) provider.error("Configuration directive is missing an ="); // extract the item name std::string name(line, stPos, eqPos-stPos); // directives without names are illegal if (name.empty()) provider.error("Configuration directive name is empty"); // Skip whitespace in the value size_t valPos = eqPos + 1; skipWhitespace(valPos, line); // Skip white space at end of value size_t endPos = line.length() - 1; while (line[endPos] == ' ') endPos--; // extract the item value std::string value(line, valPos, endPos-valPos+1); // Get the configuration item as a char pointer const char*p = name.c_str(); // Indicate we haven't found an invalid item yet. bool invalidItem = false; // Parse the contents by examining first character and // using the recognize function strategically switch (*p++) { case 'l' : // could it be "lang." if (*p == 'a') { // "lang." ? if (recognize(p,"ang")) { p++; switch (*p++) { case 'n': if (recognize(p,"ame")) data.langName = value; else invalidItem = true; break; case 't': if (recognize(p,"ranslator")) { p++; if (recognize(p,"preprocesses")) data.TranslatorPreprocesses = getBoolean(value); else if (recognize(p, "optimizes")) data.TranslatorOptimizes = getBoolean(value); else if (recognize(p, "groks_dash_O")) data.TranslatorGroksDashO = getBoolean(value); else invalidItem = true; } else invalidItem = true; break; case 'p': if (recognize(p,"reprocessor")) { p++; if (recognize(p,"needed")) { data.PreprocessorNeeded = getBoolean(value); } else invalidItem = true; } else invalidItem = true; break; default: invalidItem = true; break; } } } else if (*p == 'i') { // "linker." ? if (recognize(p,"inker")) { p++; if (recognize(p,"prog")) data.Linker.program = value; else if (recognize(p,"args")) parseArgs(data.Linker,value,provider); else invalidItem = true; } else invalidItem = true; } else { invalidItem = true; } break; case 'p' : if (*p == 'r') { // "preprocessor." ? if (recognize(p, "reprocessor")) { p++; if (recognize(p,"prog")) data.PreProcessor.program = value; else if (recognize(p,"args")) parseArgs(data.PreProcessor,value,provider); else invalidItem = true; } else invalidItem = true; } else { invalidItem = true; } break; case 't' : if (*p == 'r') { // "translator." ? if (recognize(p, "ranslator")) { p++; if (recognize(p,"prog")) data.Translator.program = value; else if (recognize(p,"args")) parseArgs(data.Translator,value,provider); else invalidItem = true; } else invalidItem = true; } else { invalidItem = true; } break; case 'o' : if (*p == 'p') { // "optimizer." ? if (recognize(p, "ptimizer")) { p++; if (recognize(p,"prog")) data.Optimizer.program = value; else if (recognize(p,"args")) parseArgs(data.Optimizer,value,provider); else invalidItem = true; } else invalidItem = true; } else { invalidItem = true; } break; case 'a' : if (*p == 's') { // "assembler." ? if (recognize(p, "ssembler")) { p++; if (recognize(p,"prog")) data.Assembler.program = value; else if (recognize(p,"args")) parseArgs(data.Assembler,value,provider); else invalidItem = true; } else invalidItem = true; } else { invalidItem = true; } break; } if (invalidItem) provider.error("Invalid configuration item: " + line.substr(stPos, eqPos-stPos)); } return new CompilerDriver::ConfigData(data); } CompilerDriver::ConfigData* ReadConfigData(const std::string& ftype) { if ( ftype == "ll" ) { StaticInputProvider sip(LL_Data, sizeof(LL_Data)/sizeof(LL_Data[0]), "LLVM Assembly (internal)"); return ParseConfigData(sip); } else if (ftype == "st") { StaticInputProvider sip(ST_Data, sizeof(ST_Data)/sizeof(ST_Data[0]), "Stacker (internal)"); return ParseConfigData(sip); } return 0; } } LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() : Configurations() , configDir() { Configurations.clear(); } LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider() { ConfigDataMap::iterator cIt = Configurations.begin(); while (cIt != Configurations.end()) { CompilerDriver::ConfigData* cd = cIt->second; ++cIt; delete cd; } Configurations.clear(); } CompilerDriver::ConfigData* LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) { CompilerDriver::ConfigData* result = 0; if (!Configurations.empty()) { ConfigDataMap::iterator cIt = Configurations.find(filetype); if ( cIt != Configurations.end() ) { // We found one in the case, return it. result = cIt->second; } } if (result == 0) { // The configuration data doesn't exist, we have to go read it. result = ReadConfigData(filetype); // If we got one, cache it if ( result != 0 ) Configurations.insert(std::make_pair(filetype,result)); } return result; // Might return 0 } // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab