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 struct exception : public std::runtime_error
{ {
exception(const Line& line, const std::string& message = std::string()) 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; 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) 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 false;
return true; 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++) for (size_t i = 0; i < lines.size(); i++)
{ {
const auto& line = lines[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(); auto directive = t.identifier();
//line.print(); //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); t.skip_spaces(true);
auto identifier = t.identifier(); auto identifier = t.identifier();
@ -308,7 +364,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
else if (directive == "else") else if (directive == "else")
{ {
if (stack.empty()) if (stack.empty())
throw std::runtime_error("no matching #if for #else"); t.error("no matching #if for #else");
if (!stack.back().value) if (!stack.back().value)
{ {
stack.back().value = true; stack.back().value = true;
@ -319,8 +375,8 @@ std::string preprocess(const std::string& input, std::string& error, const std::
else if (directive == "endif") else if (directive == "endif")
{ {
if (stack.empty()) if (stack.empty())
throw std::runtime_error("no matching #if for #endif"); t.error("no matching #if for #endif");
//("#endif (%s)\n", stack.back().condition.c_str()); //printf("#endif (%s)\n", stack.back().condition.c_str());
stack.pop_back(); stack.pop_back();
//printf("emitting: %d\n", emitting()); //printf("emitting: %d\n", emitting());
} }
@ -353,7 +409,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
t.skip_spaces(); t.skip_spaces();
} }
else 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.consume();
t.skip_spaces(); 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()); //printf("#define %s('%s' = '%s')\n", identifier.c_str(), pretty.c_str(), token.c_str());
} }
else else
{ {
// TODO: cut out comments
t.skip_spaces(); t.skip_spaces();
auto token = t.remainder(); auto token = t.remainder();
if (token.empty()) if (token.empty())
@ -406,13 +462,27 @@ std::string preprocess(const std::string& input, std::string& error, const std::
} }
else 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 else
{ {
//printf("directive: '%s'\n", directive.c_str()); //printf("directive: '%s'\n", directive.c_str());
throw std::runtime_error("unknown directive '" + directive + "'"); t.error("unknown directive '" + directive + "'");
} }
} }
else if (emitting()) else if (emitting())