#include "scanner.hh" #include "token.hh" #include "error_handler.hh" #include "object.hh" #include #include extern ErrorHandler error_handler; Scanner::Scanner(const std::string &code) : code{code}, tokens{}, start{0}, current{0}, line{1} { keywords = { std::pair("and", TokenType::AND), std::pair("class", TokenType::CLASS), std::pair("else", TokenType::ELSE), std::pair("false", TokenType::FALSE), std::pair("for", TokenType::FOR), std::pair("fun", TokenType::FUN), std::pair("if", TokenType::IF), std::pair("nil", TokenType::NIL), std::pair("or", TokenType::OR), std::pair("print", TokenType::PRINT), std::pair("return", TokenType::RETURN), std::pair("super", TokenType::SUPER), std::pair("this", TokenType::THIS), std::pair("true", TokenType::TRUE), std::pair("var", TokenType::VAR), std::pair("while", TokenType::WHILE), }; } std::vector Scanner::scan_tokens() { while (!is_at_end()) { start = current; scan_token(); } tokens.push_back(Token(TokenType::EOFILE, "", Object{}, line)); return tokens; } bool Scanner::is_at_end() { return current >= code.size(); } bool Scanner::is_digit(char c) { return c >= '0' && c <= '9'; } bool Scanner::is_alpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } bool Scanner::is_alphanumeric(char c) { return is_alpha(c) || is_digit(c); } void Scanner::scan_token() { char c = advance(); switch (c) { case '(': add_token(TokenType::LEFT_PAREN); break; case ')': add_token(TokenType::RIGHT_PAREN); break; case '{': add_token(TokenType::LEFT_BRACE); break; case '}': add_token(TokenType::RIGHT_BRACE); break; case ',': add_token(TokenType::COMMA); break; case '.': add_token(TokenType::DOT); break; case '-': add_token(TokenType::MINUS); break; case '+': add_token(TokenType::PLUS); break; case ';': add_token(TokenType::SEMICOLON); break; case '*': add_token(TokenType::STAR); break; case '!': add_token(match('=') ? TokenType::BANG_EQUAL : TokenType::BANG); break; case '=': add_token(match('=') ? TokenType::EQUAL_EQUAL : TokenType::EQUAL); break; case '<': add_token(match('=') ? TokenType::LESS_EQUAL : TokenType::LESS); break; case '>': add_token(match('=') ? TokenType::GREATER_EQUAL : TokenType::GREATER); break; case '/': if (match('/')) { while (peek() != '\n' && !is_at_end()) { advance(); } } else { add_token(TokenType::SLASH); } break; case ' ': case '\r': case '\t': break; case '\n': ++line; break; case '"': string(); break; default: if (is_digit(c)) { number(); } else if(is_alpha(c)) { identifier(); } else { error_handler.error(line, "Unexpected character."); } break; } } char Scanner::advance() { return code.at(current++); } bool Scanner::match(char expected) { if (is_at_end() || peek() != expected) { return false; } current++; return true; } char Scanner::peek() { if (is_at_end()) { return '\0'; } return code.at(current); } char Scanner::peek_next() { if (current + 1 >= code.size()) { return '\0'; } return code.at(current + 1); } void Scanner::add_token(TokenType type) { add_token(type, Object{}); } void Scanner::add_token(TokenType type, Object literal) { std::string text = code.substr(start, current - start); tokens.push_back(Token(type, text, literal, line)); } void Scanner::string() { while (peek() != '"' && !is_at_end()) { if (peek() == '\n') line++; advance(); } if (is_at_end()) { error_handler.error(line, "Unterminated string."); return; } // The closing ". advance(); // Trim the surrounding quotes. size_t no_quote_start = start + 1; size_t no_quote_end = current - 1; std::string value = code.substr(no_quote_start, no_quote_end - no_quote_start); add_token(TokenType::STRING, Object(StringObjectType::LITERAL, value)); } void Scanner::number() { while (is_digit(peek())) { advance(); } if (peek() == '.' && is_digit(peek_next())) { // Consume the "." advance(); while (is_digit(peek())) { advance(); } } std::string value = code.substr(start, current - start); add_token(TokenType::NUMBER, Object(std::stod(value))); } void Scanner::identifier() { while (is_alphanumeric(peek())) { advance(); } std::string value = code.substr(start, current - start); auto it = keywords.find(value); if (it != keywords.end()) { TokenType type = it->second; add_token(type); } else { add_token(TokenType::IDENTIFIER, Object(StringObjectType::IDENTIFIER, value)); } }