Converted to use flex for tokenizing input so we can use an easier to

understand recursive descent parser, we can easily handle more syntax
variety, and we can more easily change the configuration items accepted.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@15732 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Reid Spencer
2004-08-14 09:37:15 +00:00
parent a3f185577c
commit 68fb37ad67
9 changed files with 754 additions and 725 deletions
+247 -356
View File
@@ -13,391 +13,282 @@
//===------------------------------------------------------------------------===
#include "ConfigData.h"
#include "ConfigLexer.h"
#include "CompilerDriver.h"
#include "Support/StringExtras.h"
#include <iostream>
#include <fstream>
using namespace llvm;
extern int ::Configlex();
namespace llvm {
ConfigLexerInfo ConfigLexerData;
InputProvider* ConfigLexerInput = 0;
unsigned ConfigLexerLine = 1;
InputProvider::~InputProvider() {}
void InputProvider::error(const std::string& msg) {
std::cerr << name << ":" << ConfigLexerLine << ": Error: " << msg << "\n";
errCount++;
}
void InputProvider::checkErrors() {
if (errCount > 0) {
std::cerr << name << " had " << errCount << " errors. Terminating.\n";
exit(errCount);
}
}
}
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@"
};
class FileInputProvider : public InputProvider {
public:
FileInputProvider(const std::string & fname)
: InputProvider(fname)
, F(fname.c_str()) {
ConfigLexerInput = this;
}
virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; }
virtual unsigned read(char *buffer, unsigned max_size) {
if (F.good()) {
F.read(buffer,max_size);
if ( F.gcount() ) return F.gcount() - 1;
}
return 0;
}
// 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@"
};
bool okay() { return F.good(); }
private:
std::ifstream F;
};
class InputProvider {
public:
virtual bool getLine(std::string& line) = 0;
virtual void error(const std::string& msg) = 0;
virtual bool errorOccurred() = 0;
};
struct ParseContext
{
int token;
InputProvider* provider;
CompilerDriver::ConfigData* confDat;
CompilerDriver::Action* action;
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;
int next() { return token = Configlex(); }
bool next_is_real() {
token = Configlex();
return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
}
virtual void error(const std::string& msg) {
std::cerr << name << ":" << where << ": Error: " << msg << "\n";
errCount++;
void eatLineRemnant() {
while (next_is_real()) ;
}
virtual bool errorOccurred() { return errCount > 0; };
void error(const std::string& msg, bool skip = true) {
provider->error(msg);
if (skip)
eatLineRemnant();
}
private:
const char**TheData;
size_t limit;
size_t where;
std::string name;
size_t errCount;
};
std::string parseName() {
std::string result;
if (next() == EQUALS) {
while (next_is_real()) {
switch (token ) {
case STRING :
case OPTION :
result += ConfigLexerData.StringVal + " ";
break;
default:
error("Invalid name");
break;
}
}
if (result.empty())
error("Name exepected");
else
result.erase(result.size()-1,1);
} else
error("= expected");
return result;
}
inline bool recognize(const char*& p, const char*token) {
while (*p == *token && *token != '\0')
++token, p++;
return *token == '\0' && ((*p == '\0') || ( *p == '.' ) || (*p == '='));
}
bool parseBoolean() {
bool result = true;
if (next() == EQUALS) {
if (next() == FALSETOK) {
result = false;
} else if (token != TRUETOK) {
error("Expecting boolean value");
return false;
}
if (next() != EOLTOK && token != 0) {
error("Extraneous tokens after boolean");
}
}
else
error("Expecting '='");
return result;
}
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;
}
void parseLang() {
if ( next() == NAME ) {
confDat->langName = parseName();
} else if (token == TRANSLATOR) {
switch (next()) {
case PREPROCESSES:
confDat->TranslatorPreprocesses = parseBoolean();
break;
case OPTIMIZES:
confDat->TranslatorOptimizes = parseBoolean();
break;
case GROKS_DASH_O:
confDat->TranslatorGroksDashO = parseBoolean();
break;
default:
error("Invalid lang.translator identifier");
break;
}
}
else if (token == PREPROCESSOR) {
if (next() == NEEDED)
confDat->PreprocessorNeeded = parseBoolean();
}
else {
error("Expecting valid identifier after 'lang.'");
}
}
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;
void parseCommand(CompilerDriver::Action& action) {
if (next() == EQUALS) {
next();
if (token == EOLTOK) {
// no value (valid)
action.program.clear();
action.args.clear();
action.inputAt = 0;
action.outputAt = 0;
} else {
if (token == STRING || token == OPTION) {
action.program = ConfigLexerData.StringVal;
} 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;
}
error("Expecting a program name");
}
while (next_is_real()) {
if (token == STRING || token == OPTION)
action.args.push_back(ConfigLexerData.StringVal);
else if (token == IN_SUBST) {
action.inputAt = action.args.size();
action.args.push_back("in");
} else if (token == OUT_SUBST) {
action.outputAt = action.args.size();
action.args.push_back("out");
} else
error("Expecting a program argument", false);
}
}
break;
default :
p++;
break;
}
}
void parsePreProcessor() {
if (next() != COMMAND) {
error("Expecting 'command'");
return;
}
parseCommand(confDat->PreProcessor);
}
void parseTranslator() {
if (next() != COMMAND) {
error("Expecting 'command'");
return;
}
parseCommand(confDat->Translator);
}
void parseOptimizer() {
if (next() != COMMAND) {
error("Expecting 'command'");
return;
}
parseCommand(confDat->Optimizer);
}
void parseAssembler() {
if (next() != COMMAND) {
error("Expecting 'command'");
return;
}
parseCommand(confDat->Assembler);
}
void parseLinker() {
if (next() != COMMAND) {
error("Expecting 'command'");
return;
}
parseCommand(confDat->Linker);
}
void parseAssignment() {
switch (token) {
case LANG: return parseLang();
case PREPROCESSOR: return parsePreProcessor();
case TRANSLATOR: return parseTranslator();
case OPTIMIZER: return parseOptimizer();
case ASSEMBLER: return parseAssembler();
case LINKER: return parseLinker();
case EOLTOK: break; // just ignore
case ERRORTOK:
default:
error("Invalid top level configuration item identifier");
}
}
void parseFile() {
while ( next() != 0 ) {
parseAssignment();
}
provider->checkErrors();
}
};
void
ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
ParseContext ctxt;
ctxt.token = 0;
ctxt.provider = &provider;
ctxt.confDat = &confDat;
ctxt.action = 0;
ctxt.parseFile();
}
}
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;
LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
CompilerDriver::ConfigData* result = 0;
if (configDir.empty()) {
FileInputProvider fip( std::string("/etc/llvm/") + ftype );
if (!fip.okay()) {
fip.error("Configuration for '" + ftype + "' is not available.");
fip.checkErrors();
}
else {
result = new CompilerDriver::ConfigData();
ParseConfigData(fip,*result);
}
} else {
FileInputProvider fip( configDir + "/" + ftype );
if (!fip.okay()) {
fip.error("Configuration for '" + ftype + "' is not available.");
fip.checkErrors();
}
else {
result = new CompilerDriver::ConfigData();
ParseConfigData(fip,*result);
}
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;
}
return result;
}
LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider()