Switch to using header guards instead of pragma once
This commit is contained in:
41
cclox_src/scanner/object.cc
Normal file
41
cclox_src/scanner/object.cc
Normal 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;
|
||||
}
|
||||
38
cclox_src/scanner/object.hh
Normal file
38
cclox_src/scanner/object.hh
Normal 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
|
||||
205
cclox_src/scanner/scanner.cc
Normal file
205
cclox_src/scanner/scanner.cc
Normal 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));
|
||||
}
|
||||
}
|
||||
34
cclox_src/scanner/scanner.hh
Normal file
34
cclox_src/scanner/scanner.hh
Normal 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
133
cclox_src/scanner/token.cc
Normal 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;
|
||||
}
|
||||
44
cclox_src/scanner/token.hh
Normal file
44
cclox_src/scanner/token.hh
Normal 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
|
||||
Reference in New Issue
Block a user