stela/parser.cpp
2024-07-19 01:46:30 +02:00

155 lines
4.6 KiB
C++

#include "parser.hpp"
#include <cstdlib>
using namespace stela;
static inline auto in_range(char ch, char begin, char end) -> bool
{
return ch >= begin && ch <= end;
}
static inline auto whitespace_char(char ch) -> bool
{
return ch == ' ' or ch == '\t' or ch == '\n';
}
static inline auto id_start_char(char ch) -> bool
{
return in_range(ch, 'a', 'z') or in_range(ch, 'A', 'Z') or ch == '_';
}
static inline auto id_char(char ch) -> bool
{
return in_range(ch, 'a', 'z') or in_range(ch, 'A', 'Z')
or in_range(ch, '0', '1') or ch == '_';
}
auto Lexer::next() -> Token
{
auto pos = this->pos();
if (done()) {
return token(TokenType::Eof, pos);
}
char ch = current();
if (whitespace_char(ch)) {
step();
return next();
}
if (id_start_char(ch)) {
std::string value;
value.push_back(ch);
step();
while (not done() and id_char(current())) {
value.push_back(current());
step();
}
if (this->keyword_map.contains(value)) {
return token(this->keyword_map[value], pos);
}
size_t id = this->symbol_values.size();
this->symbol_values.push_back(value);
return Token { TokenType::Id, pos, id };
}
if (in_range(ch, '1', '9')) {
std::string value;
value.push_back(ch);
step();
while (not done() and in_range(ch, '0', '9')) {
value.push_back(current());
step();
}
int64_t int_value = std::strtoll(value.c_str(), nullptr, 10);
size_t id = this->symbol_values.size();
this->int_values.push_back(int_value);
return Token { TokenType::Id, pos, id };
}
if (ch == '0') {
step();
int64_t int_value = 0;
size_t id = this->symbol_values.size();
this->int_values.push_back(int_value);
return Token { TokenType::Id, pos, id };
}
if (ch == '"') {
// TODO string
}
if (ch == '#') {
while (not done() and current() != '\n') {
step();
}
return next();
}
if (ch == '/') {
step();
if (current() == '/') {
while (not done() and current() != '\n') {
step();
}
return next();
}
return error_token(pos, "'/' not implemented");
}
if (ch == '-') {
step();
if (not done() and current() == '>') {
step();
return token(TokenType::MinusLt, pos);
}
return token(TokenType::Minus, pos);
}
if (ch == ':') {
step();
if (current() == ':') {
step();
return token(TokenType::ColonColon, pos);
}
return token(TokenType::Colon, pos);
}
switch (ch) {
case '(':
return single_token(TokenType::LParen, pos);
case ')':
return single_token(TokenType::RParen, pos);
case '{':
return single_token(TokenType::LBrace, pos);
case '}':
return single_token(TokenType::RBrace, pos);
case '[':
return single_token(TokenType::LBracket, pos);
case ']':
return single_token(TokenType::RBracket, pos);
case '.':
return single_token(TokenType::Dot, pos);
case ',':
return single_token(TokenType::Comma, pos);
case ';':
return single_token(TokenType::Semicolon, pos);
}
step();
return error_token(pos, "unrecognized character");
}
auto Lexer::populate_keyword_map()
{
this->keyword_map["error"] = TokenType::Error;
this->keyword_map["eof"] = TokenType::Eof;
this->keyword_map["if"] = TokenType::If;
this->keyword_map["else"] = TokenType::Else;
this->keyword_map["return"] = TokenType::Return;
this->keyword_map["public"] = TokenType::Public;
this->keyword_map["private"] = TokenType::Private;
this->keyword_map["class"] = TokenType::Class;
this->keyword_map["derivable"] = TokenType::Derivable;
this->keyword_map["derives"] = TokenType::Derives;
this->keyword_map["enumeration"] = TokenType::Enumeration;
this->keyword_map["associate"] = TokenType::Associate;
this->keyword_map["attribute"] = TokenType::Attribute;
this->keyword_map["operation"] = TokenType::Operation;
this->keyword_map["state_machine"] = TokenType::StateMachine;
this->keyword_map["transition"] = TokenType::Transition;
this->keyword_map["initial"] = TokenType::Initial;
this->keyword_map["final"] = TokenType::Final;
this->keyword_map["entry"] = TokenType::Entry;
this->keyword_map["exit"] = TokenType::Exit;
}