Switch to using header guards instead of pragma once

This commit is contained in:
2025-06-08 20:25:19 +01:00
parent 9cbb61939d
commit aa800e4201
11 changed files with 38 additions and 7 deletions

View File

@@ -0,0 +1,41 @@
#include "object.hh"
#include <string>
std::ostream &operator<<(std::ostream &os, const ObjectType &type) {
switch (type) {
case ObjectType::NIL:
os << "ObjectType::NIL";
break;
case ObjectType::IDENTIFIER:
os << "ObjectType::IDENTIFIER";
break;
case ObjectType::STRING_LIT:
os << "ObjectType::STRING_LIT";
break;
case ObjectType::NUMBER:
os << "ObjectType::NUMBER";
break;
}
return os;
}
std::ostream &operator<<(std::ostream &os, const Object &obj) {
os << "Object(" << obj.type;
switch (obj.type) {
case ObjectType::NIL:
break;
case ObjectType::IDENTIFIER:
case ObjectType::STRING_LIT:
os << ", " << std::get<std::string>(obj.value);
break;
case ObjectType::NUMBER:
os << ", " << std::get<double>(obj.value);
break;
}
os << ')';
return os;
}

View File

@@ -0,0 +1,38 @@
#ifndef OBJECT_HH
#define OBJECT_HH
#include <cstdint>
#include <iostream>
#include <variant>
enum class StringObjectType : uint8_t {
IDENTIFIER,
LITERAL,
};
enum class ObjectType : uint8_t {
NIL,
IDENTIFIER,
STRING_LIT,
NUMBER,
};
struct Object {
Object() : type{ObjectType::NIL}, value{std::monostate{}} {};
Object(double number) : type{ObjectType::NUMBER}, value{number} {};
Object(StringObjectType string_type, std::string value)
: type{
string_type == StringObjectType::IDENTIFIER ?
ObjectType::IDENTIFIER :
ObjectType::STRING_LIT
},
value{std::move(value)} {};
ObjectType type;
std::variant<std::monostate, std::string, double> value;
};
std::ostream &operator<<(std::ostream &os, const ObjectType &type);
std::ostream &operator<<(std::ostream &os, const Object &obj);
#endif

View File

@@ -0,0 +1,205 @@
#include "scanner.hh"
#include "token.hh"
#include "object.hh"
#include "../error_handler.hh"
#include <string>
#include <vector>
extern ErrorHandler error_handler;
Scanner::Scanner(const std::string &code) : code{code}, tokens{}, start{0}, current{0}, line{1} {
keywords = {
std::pair<std::string, TokenType>("and", TokenType::AND),
std::pair<std::string, TokenType>("class", TokenType::CLASS),
std::pair<std::string, TokenType>("else", TokenType::ELSE),
std::pair<std::string, TokenType>("false", TokenType::FALSE),
std::pair<std::string, TokenType>("for", TokenType::FOR),
std::pair<std::string, TokenType>("fun", TokenType::FUN),
std::pair<std::string, TokenType>("if", TokenType::IF),
std::pair<std::string, TokenType>("nil", TokenType::NIL),
std::pair<std::string, TokenType>("or", TokenType::OR),
std::pair<std::string, TokenType>("print", TokenType::PRINT),
std::pair<std::string, TokenType>("return", TokenType::RETURN),
std::pair<std::string, TokenType>("super", TokenType::SUPER),
std::pair<std::string, TokenType>("this", TokenType::THIS),
std::pair<std::string, TokenType>("true", TokenType::TRUE),
std::pair<std::string, TokenType>("var", TokenType::VAR),
std::pair<std::string, TokenType>("while", TokenType::WHILE),
};
}
std::vector<Token> 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));
}
}

View File

@@ -0,0 +1,34 @@
#ifndef SCANNER_HH
#define SCANNER_HH
#include "token.hh"
#include <cstddef>
#include <vector>
#include <string>
#include <unordered_map>
struct Scanner {
Scanner(const std::string &code);
std::vector<Token> scan_tokens();
bool is_at_end();
bool is_digit(char c);
bool is_alpha(char c);
bool is_alphanumeric(char c);
void scan_token();
char advance();
bool match(char expected);
char peek();
char peek_next();
void add_token(TokenType type);
void add_token(TokenType type, Object literal);
void string();
void number();
void identifier();
std::string code;
std::vector<Token> tokens;
std::size_t start, current, line;
std::unordered_map<std::string, TokenType> keywords;
};
#endif

133
cclox_src/scanner/token.cc Normal file
View File

@@ -0,0 +1,133 @@
#include "token.hh"
#include <stdint.h>
std::ostream &operator<<(std::ostream &os, const TokenType &type) {
switch (type) {
case TokenType::LEFT_PAREN:
os << "TokenType::LEFT_PAREN";
break;
case TokenType::RIGHT_PAREN:
os << "TokenType::RIGHT_PAREN";
break;
case TokenType::LEFT_BRACE:
os << "TokenType::LEFT_BRACE";
break;
case TokenType::RIGHT_BRACE:
os << "TokenType::RIGHT_BRACE";
break;
case TokenType::COMMA:
os << "TokenType::COMMA";
break;
case TokenType::DOT:
os << "TokenType::DOT";
break;
case TokenType::MINUS:
os << "TokenType::MINUS";
break;
case TokenType::PLUS:
os << "TokenType::PLUS";
break;
case TokenType::SEMICOLON:
os << "TokenType::SEMICOLON";
break;
case TokenType::SLASH:
os << "TokenType::SLASH";
break;
case TokenType::STAR:
os << "TokenType::STAR";
break;
case TokenType::BANG:
os << "TokenType::BANG";
break;
case TokenType::BANG_EQUAL:
os << "TokenType::BANG_EQUAL";
break;
case TokenType::EQUAL:
os << "TokenType::EQUAL";
break;
case TokenType::EQUAL_EQUAL:
os << "TokenType::EQUAL_EQUAL";
break;
case TokenType::GREATER:
os << "TokenType::GREATER";
break;
case TokenType::GREATER_EQUAL:
os << "TokenType::GREATER_EQUAL";
break;
case TokenType::LESS:
os << "TokenType::LESS";
break;
case TokenType::LESS_EQUAL:
os << "TokenType::LESS_EQUAL";
break;
case TokenType::IDENTIFIER:
os << "TokenType::IDENTIFIER";
break;
case TokenType::STRING:
os << "TokenType::STRING";
break;
case TokenType::NUMBER:
os << "TokenType::NUMBER";
break;
case TokenType::AND:
os << "TokenType::AND";
break;
case TokenType::CLASS:
os << "TokenType::CLASS";
break;
case TokenType::ELSE:
os << "TokenType::ELSE";
break;
case TokenType::FALSE:
os << "TokenType::FALSE";
break;
case TokenType::FUN:
os << "TokenType::FUN";
break;
case TokenType::FOR:
os << "TokenType::FOR";
break;
case TokenType::IF:
os << "TokenType::IF";
break;
case TokenType::NIL:
os << "TokenType::NIL";
break;
case TokenType::OR:
os << "TokenType::OR";
break;
case TokenType::PRINT:
os << "TokenType::PRINT";
break;
case TokenType::RETURN:
os << "TokenType::RETURN";
break;
case TokenType::SUPER:
os << "TokenType::SUPER";
break;
case TokenType::THIS:
os << "TokenType::THIS";
break;
case TokenType::TRUE:
os << "TokenType::TRUE";
break;
case TokenType::VAR:
os << "TokenType::VAR";
break;
case TokenType::WHILE:
os << "TokenType::WHILE";
break;
case TokenType::EOFILE:
os << "TokenType::EOFILE";
break;
}
return os;
}
std::ostream &operator<<(std::ostream &os, const Token &token) {
os << token.type << ' ';
os << token.lexeme << ' ';
os << token.literal;
return os;
}

View File

@@ -0,0 +1,44 @@
#ifndef TOKEN_HH
#define TOKEN_HH
#include "object.hh"
#include <cstdint>
#include <string>
#include <utility>
enum class TokenType : uint8_t {
// Single-character tokens.
LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE,
COMMA, DOT, MINUS, PLUS, SEMICOLON, SLASH, STAR,
// One or two character tokens.
BANG, BANG_EQUAL,
EQUAL, EQUAL_EQUAL,
GREATER, GREATER_EQUAL,
LESS, LESS_EQUAL,
// Literals.
IDENTIFIER, STRING, NUMBER,
// Keywords.
AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NIL, OR,
PRINT, RETURN, SUPER, THIS, TRUE, VAR, WHILE,
EOFILE,
};
struct Token {
Token(TokenType type, std::string lexeme, Object literal, int line)
: type{type}, lexeme{std::move(lexeme)}, literal{literal}, line{line}
{};
TokenType type;
std::string lexeme;
Object literal;
int line;
};
std::ostream &operator<<(std::ostream &os, const TokenType &type);
std::ostream &operator<<(std::ostream &os, const Token &token);
#endif