roll own parser
This commit is contained in:
parent
ff45df6bc1
commit
a307f3578a
5
.clangd
Normal file
5
.clangd
Normal file
@ -0,0 +1,5 @@
|
||||
CompileFlags:
|
||||
Remove:
|
||||
- '-fmodules-ts'
|
||||
- '-fmodule-mapper=CMakeFiles/stela.dir/main.cpp.o.modmap'
|
||||
- '-fdeps-format=p1689r5'
|
154
parser.cpp
Normal file
154
parser.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
#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;
|
||||
}
|
142
parser.hpp
Normal file
142
parser.hpp
Normal file
@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace stela {
|
||||
|
||||
struct Pos {
|
||||
size_t index;
|
||||
int64_t line;
|
||||
int64_t col;
|
||||
};
|
||||
|
||||
struct Error {
|
||||
Pos pos;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
enum class TokenType {
|
||||
Error,
|
||||
Eof,
|
||||
If,
|
||||
Else,
|
||||
Return,
|
||||
Public,
|
||||
Private,
|
||||
Class,
|
||||
Derivable,
|
||||
Derives,
|
||||
Enumeration,
|
||||
Associate,
|
||||
Attribute,
|
||||
Operation,
|
||||
StateMachine,
|
||||
Transition,
|
||||
Initial,
|
||||
Final,
|
||||
Entry,
|
||||
Exit,
|
||||
LParen,
|
||||
RParen,
|
||||
LBrace,
|
||||
RBrace,
|
||||
LBracket,
|
||||
RBracket,
|
||||
Dot,
|
||||
Comma,
|
||||
Semicolon,
|
||||
Colon,
|
||||
ColonColon,
|
||||
Minus,
|
||||
MinusLt,
|
||||
Equal,
|
||||
Id,
|
||||
Int,
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
Pos pos;
|
||||
uint64_t id;
|
||||
};
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
Lexer(std::ifstream& file, std::vector<Error>& errors)
|
||||
: file(&file)
|
||||
, current_char(file.get())
|
||||
, errors(&errors)
|
||||
{ }
|
||||
|
||||
auto next() -> Token;
|
||||
|
||||
private:
|
||||
inline auto step()
|
||||
{
|
||||
if (this->current_char == EOF) {
|
||||
this->eof_reached = true;
|
||||
return;
|
||||
}
|
||||
if (this->current_char == '\n') {
|
||||
this->line += 1;
|
||||
this->col = 1;
|
||||
} else {
|
||||
this->col += 1;
|
||||
}
|
||||
this->current_char = this->file->get();
|
||||
this->index += 1;
|
||||
}
|
||||
|
||||
inline auto single_token(TokenType type, Pos pos) -> Token
|
||||
{
|
||||
step();
|
||||
return token(type, pos);
|
||||
}
|
||||
inline auto error_token(Pos pos, std::string message) const -> Token
|
||||
{
|
||||
this->errors->push_back(Error { pos, message });
|
||||
return token(TokenType::Error, pos);
|
||||
}
|
||||
inline auto token(TokenType type, Pos pos) const -> Token
|
||||
{
|
||||
return Token { type, pos, 0 };
|
||||
}
|
||||
inline auto pos() const -> Pos
|
||||
{
|
||||
return Pos {
|
||||
.index = this->index,
|
||||
.line = this->line,
|
||||
.col = this->col,
|
||||
};
|
||||
}
|
||||
|
||||
inline auto done() const -> bool { return this->eof_reached; }
|
||||
inline auto current() const -> char
|
||||
{
|
||||
return static_cast<char>(this->current_char);
|
||||
}
|
||||
|
||||
auto populate_keyword_map();
|
||||
|
||||
bool eof_reached = false;
|
||||
|
||||
size_t index = 0;
|
||||
int64_t line = 1;
|
||||
int64_t col = 1;
|
||||
|
||||
std::ifstream* file;
|
||||
int current_char;
|
||||
|
||||
std::unordered_map<std::string, TokenType> keyword_map {};
|
||||
|
||||
std::vector<std::string> symbol_values {};
|
||||
std::vector<int64_t> int_values {};
|
||||
|
||||
std::vector<Error>* errors;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user