feat(furc): implement lexer

This commit is contained in:
2026-08-05 12:34:58 +02:00
parent 4604f5186e
commit 3faafe371f
5 changed files with 297 additions and 8 deletions
+154
View File
@@ -0,0 +1,154 @@
#include "furc/front/lexer.hpp"
#include <cctype>
#include <string_view>
#include <unordered_map>
namespace furc {
token lexer::next_token() {
if (m_peekToken.has_value()) {
auto tok = m_peekToken.value();
m_peekToken = {};
return tok;
}
skip_spaces();
if (m_cursor >= m_content.size()) return { location(), token::EndOfFile };
auto loc = location();
if (std::isdigit(get()) != 0) {}
if (std::isalnum(get()) != 0 || get() == '_') {
std::size_t begin = m_cursor;
next();
while (m_cursor < m_content.size() && (std::isalnum(get()) != 0 || get() == '_'))
next();
return { loc, token::Identifier, m_content.substr(begin, m_cursor - begin) };
}
if (get() == '"') {
next();
std::size_t begin = m_cursor;
while (m_cursor < m_content.size() && get() != '"')
next();
if (m_cursor >= m_content.size()) return { location(), token::UnexpectedEOF };
next();
return { loc, token::String, m_content.substr(begin, m_cursor - begin - 1) };
}
if (get() == '\'') {
next();
bool slash = get() == '\\';
if (slash) next();
auto loc2 = location();
char character = get();
next();
if (get() != '\'') return { location(), token::UnexpectedCharacter, get() };
next();
if (slash) {
switch (character) {
case '\\': character = '\\'; break;
case 'n': character = '\n'; break;
case 'r': character = '\r'; break;
case 't': character = '\t'; break;
default: return { loc2, token::UnexpectedCharacter, character };
}
}
return { loc, token::Char, character };
}
static std::unordered_map<std::string_view, token_t> s_tokens = {
{ "(", token::LParen },
{ ")", token::RParen },
{ "{", token::LBrace },
{ "}", token::RBrace },
{ "[", token::LBracket },
{ "]", token::RBracket },
{ ";", token::Semicolon },
{ ":", token::Colon },
{ ",", token::Comma },
{ ".", token::Dot },
{ "+", token::Plus },
{ "-", token::Minus },
{ "*", token::Star },
{ "/", token::Slash },
{ "%", token::Percent },
{ "&", token::Ampersand },
{ "|", token::Pipe },
{ "^", token::Hat },
{ "&&", token::DblAmpersand },
{ "||", token::DblPipe },
{ "++", token::DblPlus },
{ "--", token::DblMinus },
{ "!", token::ExMark },
{ "^^", token::CatEars },
{ "=", token::Equals },
{ "+=", token::PlusEquals },
{ "-=", token::MinusEquals },
{ "*=", token::StarEquals },
{ "/=", token::SlashEquals },
{ "%=", token::PercentEquals },
{ "&=", token::AmpersandEquals },
{ "|=", token::PipeEquals },
{ "^=", token::HatEquals },
{ "==", token::DblEquals },
{ "!=", token::ExEquals },
{ "<", token::LessThan },
{ "<=", token::LessEquals },
{ ">", token::GreaterThan },
{ ">=", token::GreaterEquals },
{ "->", token::SlimArrow },
{ "=>", token::FatArrow },
{ "@", token::Monkey },
{ "#", token::Sha256 },
};
std::size_t begin = m_cursor;
std::size_t len = 1;
while (begin + len - 1 < m_content.size() && s_tokens.find(m_content.substr(begin, len)) != s_tokens.end())
++len;
if (len > 1) {
auto type = s_tokens[m_content.substr(begin, len - 1)];
m_cursor += len - 1;
return { loc, type };
}
return { loc, token::UnexpectedCharacter, get() };
}
token lexer::peek_token() {
if (m_peekToken.has_value()) return m_peekToken.value();
auto tok = next_token();
m_peekToken = tok;
return tok;
}
token lexer::skip_token() {
m_peekToken = {};
return next_token();
}
void lexer::next() {
if (m_cursor < m_content.size()) ++m_cursor;
}
constexpr char lexer::get(std::size_t offset) const {
return m_content[m_cursor + offset];
}
void lexer::skip_spaces() {
while (m_cursor < m_content.size() && std::isspace(get()) != 0)
++m_cursor;
}
constexpr token::location lexer::location() const {
return { m_filepath, m_row, m_cursor - m_lineStart };
}
} // namespace furc
+14 -3
View File
@@ -1,7 +1,18 @@
#include "furc/front/lexer.hpp"
#include <iostream>
int main(void) {
std::cout << "Farewell, stasiu!\n";
return 0;
furc::lexer lexer = { "<AK>", "func main(argc: u64) -> s32 { return '\\\\'; }" };
while (true) {
furc::token token = lexer.next_token();
std::cout << token.loc.filepath << ':' << token.loc.row + 1 << ':' << token.loc.col + 1 << ": " << token
<< '\n';
switch (token.type) {
case furc::token::UnexpectedCharacter:
case furc::token::UnexpectedEOF: return 1;
case furc::token::EndOfFile: return 0;
default: break;
}
}
}