Basic support for #pragma once and #if defined

This commit is contained in:
Duncan Ogilvie 2023-11-27 16:20:10 +01:00
parent b24d73c7b7
commit 962b648019
1 changed files with 79 additions and 9 deletions

View File

@ -35,7 +35,7 @@ struct Tokenizer
struct exception : public std::runtime_error
{
exception(const Line& line, const std::string& message = std::string())
: std::runtime_error(message + " === " + line.str())
: std::runtime_error(message + "\n=========\n" + line.str() + "\n=========")
{
}
};
@ -131,6 +131,34 @@ struct Tokenizer
}
return result;
}
void expect(int expected)
{
auto actual = peek();
if(actual != expected)
{
auto chstr = [](int ch)
{
std::string result;
if(ch == EOF)
{
result = "EOF";
}
else
{
result += "'";
result += (char)ch;
result += "'";
}
return result;
};
error("expected character " + chstr(expected) + ", got " + chstr(actual));
}
if(expected != EOF)
{
consume();
}
}
};
std::string remove_block_comments(const std::string& input)
@ -275,6 +303,22 @@ std::string preprocess(const std::string& input, std::string& error, const std::
return false;
return true;
};
auto evaluate = [&state](Tokenizer& t)
{
// TODO: support proper expression evaluation
auto id = t.identifier();
if(id == "defined")
{
t.expect('(');
auto name = t.identifier();
t.expect(')');
t.expect(EOF);
return state.count(name) > 0;
}
t.error("unsupported expression '" + id + "'");
return false;
};
for (size_t i = 0; i < lines.size(); i++)
{
const auto& line = lines[i];
@ -289,7 +333,19 @@ std::string preprocess(const std::string& input, std::string& error, const std::
auto directive = t.identifier();
//line.print();
if (directive == "ifndef")
if (directive == "if")
{
t.skip_spaces(true);
auto pos = t.position;
auto expression = t.remainder();
while(!expression.empty() && std::isspace(expression.back()))
expression.pop_back();
t.position = pos;
auto result = evaluate(t);
//printf("#if(%s): %d\n", expression.c_str(), result);
stack.push_back({i, expression, result});
}
else if (directive == "ifndef")
{
t.skip_spaces(true);
auto identifier = t.identifier();
@ -308,7 +364,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
else if (directive == "else")
{
if (stack.empty())
throw std::runtime_error("no matching #if for #else");
t.error("no matching #if for #else");
if (!stack.back().value)
{
stack.back().value = true;
@ -319,8 +375,8 @@ std::string preprocess(const std::string& input, std::string& error, const std::
else if (directive == "endif")
{
if (stack.empty())
throw std::runtime_error("no matching #if for #endif");
//("#endif (%s)\n", stack.back().condition.c_str());
t.error("no matching #if for #endif");
//printf("#endif (%s)\n", stack.back().condition.c_str());
stack.pop_back();
//printf("emitting: %d\n", emitting());
}
@ -353,7 +409,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
t.skip_spaces();
}
else
throw std::runtime_error("expect ',' or ')' got something else (too lazy sry)");
t.error("expect ',' or ')' got something else (too lazy sry)");
}
t.consume();
t.skip_spaces();
@ -368,10 +424,10 @@ std::string preprocess(const std::string& input, std::string& error, const std::
}
//printf("#define %s('%s' = '%s')\n", identifier.c_str(), pretty.c_str(), token.c_str());
}
else
{
// TODO: cut out comments
t.skip_spaces();
auto token = t.remainder();
if (token.empty())
@ -406,13 +462,27 @@ std::string preprocess(const std::string& input, std::string& error, const std::
}
else
{
throw std::runtime_error("invalid syntax for #include");
t.error("invalid syntax for #include");
}
}
else if(directive == "pragma")
{
t.skip_spaces(true);
auto type = t.identifier();
if(type == "once")
{
//printf("#pragma once");
// TODO: implement something?
}
else
{
t.error("unsupported #pragma type '" + type + "'");
}
}
else
{
//printf("directive: '%s'\n", directive.c_str());
throw std::runtime_error("unknown directive '" + directive + "'");
t.error("unknown directive '" + directive + "'");
}
}
else if (emitting())