#include "kappa/dsl/lexer.hpp" #include #include #include namespace kappa::dsl { static const std::unordered_map keywords = { {"package", TokenType::KwPackage}, {"version", TokenType::KwVersion}, {"source", TokenType::KwSource}, {"depends", TokenType::KwDepends}, {"provides", TokenType::KwProvides}, {"outputs", TokenType::KwOutputs}, {"conflicts", TokenType::KwConflicts}, {"features", TokenType::KwFeatures}, {"config", TokenType::KwConfig}, {"const", TokenType::KwConst}, {"license", TokenType::KwLicense}, {"patches", TokenType::KwPatches}, {"env", TokenType::KwEnv}, {"service", TokenType::KwService}, {"sha256", TokenType::KwSha256}, {"sha512", TokenType::KwSha512}, {"md5", TokenType::KwMd5}, {"assert", TokenType::KwAssert}, {"import", TokenType::KwImport}, {"prepare", TokenType::KwPrepare}, {"build", TokenType::KwBuild}, {"check", TokenType::KwCheck}, {"install", TokenType::KwInstall}, {"uninstall", TokenType::KwUninstall}, {"true", TokenType::KwTrue}, {"false", TokenType::KwFalse}, {"index", TokenType::KwIndex}, {"repos", TokenType::KwRepos}, }; std::string_view token_name(TokenType type) { switch (type) { case TokenType::Eof: return "EOF"; case TokenType::Newline: return "newline"; case TokenType::Lbrace: return "{"; case TokenType::Rbrace: return "}"; case TokenType::Equals: return "="; case TokenType::Plus: return "+"; case TokenType::Lbracket: return "["; case TokenType::Rbracket: return "]"; case TokenType::Comma: return ","; case TokenType::String: return "string"; case TokenType::Ident: return "ident"; case TokenType::KwPackage: return "package"; case TokenType::KwVersion: return "version"; case TokenType::KwSource: return "source"; case TokenType::KwDepends: return "depends"; case TokenType::KwProvides: return "provides"; case TokenType::KwOutputs: return "outputs"; case TokenType::KwConflicts: return "conflicts"; case TokenType::KwFeatures: return "features"; case TokenType::KwConfig: return "config"; case TokenType::KwConst: return "const"; case TokenType::KwLicense: return "license"; case TokenType::KwPatches: return "patches"; case TokenType::KwEnv: return "env"; case TokenType::KwService: return "service"; case TokenType::KwSha256: return "sha256"; case TokenType::KwSha512: return "sha512"; case TokenType::KwMd5: return "md5"; case TokenType::KwAssert: return "assert"; case TokenType::KwImport: return "import"; case TokenType::KwPrepare: return "prepare"; case TokenType::KwBuild: return "build"; case TokenType::KwCheck: return "check"; case TokenType::KwInstall: return "install"; case TokenType::KwUninstall: return "uninstall"; case TokenType::KwTrue: return "true"; case TokenType::KwFalse: return "false"; case TokenType::KwIndex: return "index"; case TokenType::KwRepos: return "repos"; } return "?"; } Lexer::Lexer(std::string_view source) : source_(source) {} char Lexer::peek() const { return pos_ < source_.size() ? source_[pos_] : '\0'; } char Lexer::advance() { if (pos_ >= source_.size()) { return '\0'; } char c = source_[pos_++]; if (c == '\n') { ++line_; col_ = 1; } else { ++col_; } return c; } bool Lexer::match(char c) { if (peek() == c) { advance(); return true; } return false; } void Lexer::skip_whitespace() { while (std::isspace(static_cast(peek())) && peek() != '\n') { advance(); } } bool Lexer::skip_comment() { if (peek() != '/') { return false; } if (pos_ + 1 >= source_.size()) { return false; } char next = source_[pos_ + 1]; if (next == '/') { advance(); advance(); // skip // while (pos_ < source_.size() && peek() != '\n') { advance(); } return true; } if (next == '*') { advance(); advance(); // skip /* while (pos_ + 1 < source_.size()) { if (peek() == '*' && source_[pos_ + 1] == '/') { advance(); advance(); // skip */ return true; } advance(); } return true; // unterminated — consume to EOF } return false; } Token Lexer::next() { while (true) { skip_whitespace(); if (skip_comment()) { continue; } token_start_line_ = line_; token_start_col_ = col_; if (pos_ >= source_.size()) { return {TokenType::Eof, "", line_, col_}; } char c = peek(); if (c == '\n') { advance(); return {TokenType::Newline, "\n", token_start_line_, token_start_col_}; } if (c == '"') { return scan_string(); } if (c == '{' || c == '}' || c == '=' || c == '+' || c == '[' || c == ']' || c == ',') { return scan_symbol(); } return scan_ident(); } } Token Lexer::scan_ident() { std::string lexeme; while (pos_ < source_.size()) { char c = peek(); if (std::isspace(static_cast(c))) { break; } if (c == '"' || c == '=' || c == '+' || c == '[' || c == ']' || c == ',') { break; } lexeme += advance(); } auto it = keywords.find(lexeme); if (it != keywords.end()) { return {it->second, lexeme, token_start_line_, token_start_col_}; } return {TokenType::Ident, lexeme, token_start_line_, token_start_col_}; } Token Lexer::scan_string() { advance(); // opening " std::string lexeme; while (pos_ < source_.size()) { char c = peek(); if (c == '"') { advance(); break; } if (c == '\\') { advance(); if (pos_ < source_.size()) { char esc = advance(); switch (esc) { case 'n': lexeme += '\n'; break; case 't': lexeme += '\t'; break; case '\\': lexeme += '\\'; break; case '"': lexeme += '"'; break; default: lexeme += esc; break; } } } else { lexeme += advance(); } } return {TokenType::String, lexeme, token_start_line_, token_start_col_}; } Token Lexer::scan_symbol() { char c = advance(); switch (c) { case '{': return {TokenType::Lbrace, "{", token_start_line_, token_start_col_}; case '}': return {TokenType::Rbrace, "}", token_start_line_, token_start_col_}; case '=': return {TokenType::Equals, "=", token_start_line_, token_start_col_}; case '+': return {TokenType::Plus, "+", token_start_line_, token_start_col_}; case '[': return {TokenType::Lbracket, "[", token_start_line_, token_start_col_}; case ']': return {TokenType::Rbracket, "]", token_start_line_, token_start_col_}; case ',': return {TokenType::Comma, ",", token_start_line_, token_start_col_}; default: return {TokenType::Eof, "", token_start_line_, token_start_col_}; } } } // namespace kappa::dsl