Add DEFINE preprocessor directive

This commit is contained in:
2020-05-30 12:21:50 +02:00
parent 3bb2cc17e9
commit 0286f25e8d
5 changed files with 174 additions and 29 deletions

View File

@@ -5,11 +5,6 @@
namespace Token
{
bool IsWhiteSpace(char const c)
{
return c == '\n' || c == ' ' || c == '\t' || c == '\r';
}
std::tuple<int, bool> TryParseInt(std::string const & string)
{
try
@@ -23,24 +18,42 @@ namespace Token
}
}
Token ExtractToken(std::string const & string, int const lineNumber, int const lineColumn)
Token Tokenizer::ExtractToken(std::string string,
int const lineNumber,
int const lineColumn) const
{
if (string.size() == 0)
{
return Token::CreateUnknownToken(lineNumber, lineColumn);
}
for(std::size_t i = 0; i < substitutions.size(); ++i)
{
if (string == substitutions[i].first)
{
string = substitutions[i].second;
break;
}
}
char const prefix = string[0];
switch(prefix)
{
case '$':
{
auto const result = TryParseInt(string.substr(1, string.size()));
return Token::CreateImmediateValueToken(std::get<0>(result), std::get<1>(result), lineNumber, lineColumn);
return Token::CreateImmediateValueToken(
std::get<0>(result),
std::get<1>(result),
lineNumber,
lineColumn);
}
case '%':
return Token::CreateRegisterToken(GetRegisterType(string.substr(1, string.size())), lineNumber, lineColumn);
return Token::CreateRegisterToken(GetRegisterType(
string.substr(1, string.size())),
lineNumber,
lineColumn);
case ';':
return Token::CreateStatementEndToken(lineNumber, lineColumn);
@@ -53,7 +66,11 @@ namespace Token
if (postfix == ':')
{
// TODO check if label is an Operand?
return Token::CreateLabelToken(string.substr(0, string.size() - 1), true, lineNumber, lineColumn);
return Token::CreateLabelToken(
string.substr(0, string.size() - 1),
true,
lineNumber,
lineColumn);
}
if (prefix == '[' && postfix == ']')
@@ -68,11 +85,18 @@ namespace Token
if (memoryPrefix == '$')
{
auto const result = TryParseInt(valueString);
return Token::CreateMemoryToken(std::get<0>(result), std::get<1>(result), lineNumber, lineColumn);
return Token::CreateMemoryToken(
std::get<0>(result),
std::get<1>(result),
lineNumber,
lineColumn);
}
else if (memoryPrefix == '%')
{
return Token::CreateMemoryToken(GetRegisterType(valueString), lineNumber, lineColumn);
return Token::CreateMemoryToken(
GetRegisterType(valueString),
lineNumber,
lineColumn);
}
else
{
@@ -95,7 +119,115 @@ namespace Token
return Token::CreateLabelToken(string, true, lineNumber, lineColumn);
}
void Tokenizer::Tokenize(std::string const & line, int const lineNumber, std::vector<Token> & tokens)
bool IsWhiteSpace(char const c)
{
return c == '\n' || c == ' ' || c == '\t' || c == '\r';
}
void Tokenizer::ParseComment(
std::string const & string,
int const lineNumber,
int const lineColumn)
{
unsigned const commentContentStart = lineColumn + 1;
if (string.size() < commentContentStart ||
IsWhiteSpace(string[commentContentStart]))
{
return;
}
enum class CommentParseState
{
LookForDirectiveEnd,
LookForArgumentStart,
LookForArgumentEnd
};
std::string firstArgument, secondArgument;
unsigned argumentCount = 0, argumentStart = 0;
CommentParseState state = CommentParseState::LookForDirectiveEnd;
for(unsigned i = commentContentStart + 1; i < string.size(); ++i)
{
switch(state)
{
case CommentParseState::LookForDirectiveEnd:
if(IsWhiteSpace(string[i]))
{
if (string.compare(commentContentStart, i - commentContentStart, "DEFINE"))
{
// Nonzero = not equal
return;
}
state = CommentParseState::LookForArgumentStart;
}
break;
case CommentParseState::LookForArgumentStart:
if(!IsWhiteSpace(string[i]))
{
argumentStart = i;
state = CommentParseState::LookForArgumentEnd;
}
break;
case CommentParseState::LookForArgumentEnd:
if (IsWhiteSpace(string[i]))
{
state = CommentParseState::LookForArgumentStart;
switch(argumentCount)
{
case 0:
firstArgument = string.substr(argumentStart, i - argumentStart);
break;
case 1:
secondArgument = string.substr(argumentStart, i - argumentStart);
break;
default:
goto end_state_loop;
}
++argumentCount;
}
break;
}
}
end_state_loop:
switch(state)
{
case CommentParseState::LookForDirectiveEnd:
case CommentParseState::LookForArgumentStart:
break;
case CommentParseState::LookForArgumentEnd:
switch(argumentCount)
{
case 0:
firstArgument = string.substr(argumentStart);
break;
case 1:
secondArgument = string.substr(argumentStart);
break;
}
++argumentCount;
break;
}
if (argumentCount > 0)
{
substitutions.push_back(std::make_pair(firstArgument, secondArgument));
}
else
{
std::printf("WARNING: DEFINE with no arguments on line %u\n", lineNumber + 1);
}
}
void Tokenizer::Tokenize(
std::string const & line,
int const lineNumber,
std::vector<Token> & tokens)
{
enum class TokenizerState
{
@@ -114,7 +246,7 @@ namespace Token
{
if (line[column] == '#')
{
// Ignore comments
ParseComment(line, lineNumber, column);
return;
}
@@ -123,7 +255,8 @@ namespace Token
switch(line[column])
{
case ';':
tokens.push_back(ExtractToken(line.substr(column, 1), lineNumber, column));
tokens.push_back(
ExtractToken(line.substr(column, 1), lineNumber, column));
break;
default:
@@ -154,7 +287,10 @@ namespace Token
switch(state)
{
case TokenizerState::LookForTokenEnd:
tokens.push_back(ExtractToken(line.substr(columnTokenStart, line.size()), lineNumber, columnTokenStart));
tokens.push_back(ExtractToken(
line.substr(columnTokenStart, line.size()),
lineNumber,
columnTokenStart));
break;
case TokenizerState::LookForNextToken: