auto env = std::make_shared<Environment>(closureEnv);
for (int i = 0; i < stmt->args.size(); i++) {
- env->define(stmt->args[i]->lexeme, args[i]);
+ env->define(std::get<const char *>(stmt->args[i]->lexeme), args[i]);
}
try {
#include "LBPLInstance.hpp"
#include "runtime_error.hpp"
+#include <string>
Value LBPLInstance::get(const Token *name) {
- if (fields.contains(name->lexeme)) {
- return fields.find(name->lexeme)->second;
+ const char *lexeme = std::get<const char *>(name->lexeme);
+ if (fields.contains(lexeme)) {
+ return fields.find(lexeme)->second;
}
- LBPLFunc *method = lbplClass->findMethod(name->lexeme);
+ LBPLFunc *method = lbplClass->findMethod(lexeme);
if (method) {
method->bind(std::make_shared<LBPLInstance>(this));
return std::make_shared<LBPLFunc>(*method);
}
- throw RuntimeError(name, "Undefined field '" + name->lexeme + "'.");
+ throw RuntimeError(name, "Undefined field '" + std::string(lexeme) + "'.");
}
void LBPLInstance::set(const Token *name, Value &value) {
- fields.insert_or_assign(name->lexeme, value);
+ fields.insert_or_assign(std::get<const char *>(name->lexeme), value);
}
#include "LBPLTypes.hpp"
#include "runtime_error.hpp"
+#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <variant>
-#include <iostream>
void Environment::define(const std::string &name, Value &value) {
env.insert(std::make_pair(name, value));
}
Value Environment::get(std::shared_ptr<const Token> &name) {
- auto it = env.find(name->lexeme);
+ const char *namestr = std::get<const char *>(name->lexeme);
+ auto it = env.find(namestr);
if (it != env.end()) {
return it->second;
return enclosing->get(name);
}
- throw RuntimeError(name.get(), "Undefined name '" + name->lexeme + "'.");
+ throw RuntimeError(name.get(),
+ "Undefined name '" + std::string(namestr) + "'.");
}
Value Environment::getAt(int depth, std::shared_ptr<const Token> &name) {
- return getAt(depth, name->lexeme);
+ return getAt(depth, std::get<const char *>(name->lexeme));
}
Value Environment::getAt(int depth, const std::string &name) {
}
void Environment::assign(std::shared_ptr<const Token> &name, Value &value) {
- if (env.contains(name->lexeme)) {
- env.insert_or_assign(name->lexeme, value);
+ if (auto namestr = std::get<const char *>(name->lexeme);
+ env.contains(namestr)) {
+ env.insert_or_assign(namestr, value);
} else if (enclosing) {
enclosing->assign(name, value);
} else {
throw RuntimeError(name.get(),
- "Undefined variable '" + name->lexeme + "'.");
+ "Undefined variable '" + std::string(namestr) + "'.");
}
}
enclosing->assignAt(depth - 1, name, value);
}
- env.insert_or_assign(name->lexeme, value);
+ env.insert_or_assign(std::get<const char *>(name->lexeme), value);
}
void Environment::printEnv(const std::string &&msg) {
#include "runtime_error.hpp"
#include <cstddef>
+#include <cstdint>
#include <map>
#include <memory>
#include <string>
}
void Interpreter::visitFnStmt(FnStmt *stmt) {
- currentEnv->define(stmt->name->lexeme,
+ currentEnv->define(std::get<const char *>(stmt->name->lexeme),
std::make_shared<LBPLFunc>(stmt, currentEnv, false));
}
value = stmt->value->accept(this);
}
- currentEnv->define(stmt->name->lexeme, value);
+ currentEnv->define(std::get<const char *>(stmt->name->lexeme), value);
}
void Interpreter::visitClassStmt(ClassStmt *stmt) {
Value superclass;
- currentEnv->define(stmt->name->lexeme, nullptr);
+ currentEnv->define(std::get<const char *>(stmt->name->lexeme), nullptr);
if (stmt->superclass) {
superclass = stmt->superclass->accept(this);
throw RuntimeError(methodStmt.get(), "Excepted class method.");
}
- auto fn = new LBPLFunc(method, currentEnv, method->name->lexeme == "init");
- methods.insert(std::make_pair(method->name->lexeme, fn));
+ auto namestr = std::get<const char *>(method->name->lexeme);
+ auto fn = new LBPLFunc(method, currentEnv, std::string(namestr) == "init");
+ methods.insert(std::make_pair(namestr, fn));
}
if (stmt->superclass) {
currentEnv = currentEnv->enclosing;
currentEnv->assign(stmt->name,
std::make_shared<LBPLClass>(
- stmt->name->lexeme,
+ std::get<const char *>(stmt->name->lexeme),
std::get<std::shared_ptr<LBPLClass>>(superclass),
methods));
} else {
currentEnv->assign(
- stmt->name, std::make_shared<LBPLClass>(stmt->name->lexeme, methods));
+ stmt->name, std::make_shared<LBPLClass>(
+ std::get<const char *>(stmt->name->lexeme), methods));
}
}
} else if (expr->token->type == TokenType::Nil) {
return nullptr;
} else if (expr->token->type == TokenType::Char) {
- return expr->token->lexeme[0];
+ return std::get<char>(expr->token->lexeme);
} else if (expr->token->type == TokenType::String) {
- return expr->token->lexeme;
+ return std::get<const char *>(expr->token->lexeme);
} else if (expr->token->type == TokenType::Number) {
- if (expr->token->lexeme.find('.') == std::string::npos) {
- return std::stoi(expr->token->lexeme);
+ if (std::holds_alternative<float>(expr->token->lexeme)) {
+ return std::get<float>(expr->token->lexeme);
+ } else {
+ return std::get<int32_t>(expr->token->lexeme);
}
- return std::stod(expr->token->lexeme);
} else if (expr->token->type == TokenType::Identifier) {
return lookupVariable(expr->token, expr);
}
}
Value Interpreter::visitSuperExpr(SuperExpr *) { return nullptr; }
-Value Interpreter::visitThisExpr(ThisExpr *expr) { return lookupVariable(expr->keyword, expr); }
+Value Interpreter::visitThisExpr(ThisExpr *expr) {
+ return lookupVariable(expr->keyword, expr);
+}
Value Interpreter::visitCallExpr(FnCallExpr *expr) {
Value callee = expr->callee->accept(this);
}
Value Interpreter::performBinaryOperation(std::shared_ptr<const Token> &op,
- const Value &left,
- const Value &right) {
+ const Value &left,
+ const Value &right) {
auto performIntOp = [](int l, int r,
std::shared_ptr<const Token> &op) -> Value {
switch (op->type) {
bool Interpreter::isTruthy(Value &&value) { return isTruthy(value); }
Value Interpreter::lookupVariable(std::shared_ptr<const Token> &name,
- Expr *expr) {
+ Expr *expr) {
auto it = locals.find(expr);
if (it == locals.end()) {
#include "lexer.hpp"
+#include "token.hpp"
+#include "token_type.hpp"
+
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <memory>
+#include <string_view>
+
+#define MAKE_TOKEN(TYPE, LEXEME) \
+ std::make_shared<const Token>(TYPE, LEXEME, file.line, file.column, \
+ file.filepath)
+
+namespace Lexer {
+std::shared_ptr<const Token> getNextToken(Source &file) {
+ if (file.stream.peek() == EOF) {
+ return MAKE_TOKEN(TokenType::Eof, 0);
+ }
-Lexer::Lexer(const char *stream, const std::string &filename)
- : line(1), hadError(false), filename(filename), current(stream), start(stream) {}
+ skipWhitespace(file);
+ char ch = file.stream.peek();
-bool Lexer::isAtEnd() const { return *current == '\0'; }
-bool Lexer::isDigit(char ch) const { return ch >= '0' && ch <= '9'; }
-bool Lexer::isAlpha(char ch) const {
- return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_';
-}
+ if (isDigit(ch)) {
+ return makeNumberToken(file);
+ } else if (isAlpha(ch)) {
+ return makeIdentifierToken(file);
+ }
-bool Lexer::match(char ch) {
- if (isAtEnd() || ch != *current) {
- return false;
+ switch ((ch = file.advance())) {
+ case '(':
+ return MAKE_TOKEN(TokenType::LeftParen, 0);
+ case ')':
+ return MAKE_TOKEN(TokenType::RightParen, 0);
+ case '{':
+ return MAKE_TOKEN(TokenType::LeftBrace, 0);
+ case '}':
+ return MAKE_TOKEN(TokenType::RightBrace, 0);
+ case '?':
+ return MAKE_TOKEN(TokenType::Question, 0);
+ case ',':
+ return MAKE_TOKEN(TokenType::Comma, 0);
+ case '.':
+ return MAKE_TOKEN(TokenType::Dot, 0);
+ case ':':
+ return MAKE_TOKEN(TokenType::Colon, 0);
+ case ';':
+ return MAKE_TOKEN(TokenType::Semicolon, 0);
+ case '%':
+ return MAKE_TOKEN(TokenType::ModOp, 0);
+ case '&':
+ if (char _ch = file.stream.peek(); _ch == '&') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::And, 0);
+ } else {
+ std::string str_msg = "invalid token '" + std::string(1, _ch) + "'";
+ char *msg = (char *)malloc(str_msg.size() + 1);
+ std::strncpy(msg, str_msg.c_str(), str_msg.size() + 1);
+ return MAKE_TOKEN(TokenType::Error, msg);
+ }
+ case '|':
+ if (char _ch = file.stream.peek(); _ch == '|') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::Or, 0);
+ } else {
+ std::string str_msg = "invalid token '" + std::string(1, _ch) + "'";
+ char *msg = (char *)malloc(str_msg.size() + 1);
+ std::strncpy(msg, str_msg.c_str(), str_msg.size() + 1);
+ return MAKE_TOKEN(TokenType::Error, msg);
+ }
+ case '-':
+ return MAKE_TOKEN(TokenType::Minus, 0);
+ case '+':
+ return MAKE_TOKEN(TokenType::Plus, 0);
+ case '/':
+ return MAKE_TOKEN(TokenType::Slash, 0);
+ case '*':
+ return MAKE_TOKEN(TokenType::Star, 0);
+ case '!':
+ if (file.stream.peek() == '=') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::BangEqual, 0);
+ } else {
+ return MAKE_TOKEN(TokenType::Bang, 0);
+ }
+ case '=':
+ if (file.stream.peek() == '=') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::EqualEqual, 0);
+ } else {
+ return MAKE_TOKEN(TokenType::Equal, 0);
+ }
+ case '>':
+ if (file.stream.peek() == '>') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::ShiftRight, 0);
+ } else if (file.stream.peek() == '=') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::GreaterEqual, 0);
+ } else {
+ return MAKE_TOKEN(TokenType::Greater, 0);
+ }
+ case '<':
+ if (file.stream.peek() == '<') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::ShiftLeft, 0);
+ } else if (file.stream.peek() == '=') {
+ file.advance();
+ return MAKE_TOKEN(TokenType::LessEqual, 0);
+ } else {
+ return MAKE_TOKEN(TokenType::Less, 0);
+ }
+ case '\'': {
+ char lexeme = file.advance();
+ if (file.stream.peek() != '\'') {
+ return MAKE_TOKEN(TokenType::Error, "A char must be one character long.");
+ } else {
+ return MAKE_TOKEN(TokenType::Char, lexeme);
+ }
}
+ case '"': {
+ size_t len = 0;
+ char *lexeme = 0;
+ char new_ch = 0;
- advance();
- return true;
-}
+ while (file.stream.eof() && file.stream.peek() != '"') {
+ if (file.stream.peek() == '\\') {
+ file.advance();
-char Lexer::peek() const { return *current; }
-char Lexer::peekNext() const { return *(current + 1); }
+ switch (file.stream.peek()) {
+ case 'n':
+ new_ch = '\n';
+ break;
+ case 't':
+ new_ch = '\t';
+ break;
+ case 'r':
+ new_ch = '\r';
+ break;
+ case '\'':
+ new_ch = '\'';
+ break;
+ case '\"':
+ new_ch = '"';
+ break;
+ case '\\':
+ new_ch = '\\';
+ break;
+ default:
+ return MAKE_TOKEN(TokenType::Error, "invalid escape sequence");
+ }
-char Lexer::advance() {
- if (isAtEnd()) {
- return '\0';
+ file.advance();
+ } else {
+ new_ch = file.advance();
+ }
+
+ lexeme = (char *)realloc(lexeme, ++len);
+ lexeme[len - 1] = new_ch;
+ }
+
+ if (file.stream.eof()) {
+ return MAKE_TOKEN(TokenType::Error, "Unterminated string.");
+ } else {
+ file.advance();
+ return MAKE_TOKEN(TokenType::String, lexeme);
+ }
+ }
+ }
+
+ if (file.stream.eof()) {
+ return MAKE_TOKEN(TokenType::Eof, 0);
}
- return *(current++);
+ return MAKE_TOKEN(TokenType::Error, "\033[1;36mHow did you get here?\033[0m");
}
-TokenType Lexer::checkKeyword(int startIndex, const std::string &restOfKeyword,
- TokenType typeIfMatch) {
- if (current - (start + startIndex) != restOfKeyword.length()) {
- return TokenType::Identifier;
+void skipWhitespace(Source &file) {
+ while (!file.stream.eof()) {
+ switch (file.stream.peek()) {
+ case '\r':
+ case '\n': {
+ file.line++;
+ file.advance();
+ file.column = 0;
+ } break;
+ case ' ': {
+ file.advance();
+ } break;
+ case '\t': {
+ file.column += 3;
+ file.advance();
+ } break;
+ case '#': {
+ while (file.stream.peek() != '\n' || file.stream.peek() != '\r') {
+ file.advance();
+ }
+ } break;
+ default:
+ return;
+ }
}
+}
- for (int i = 0; i < restOfKeyword.length(); i++) {
- if (*(start + startIndex + i) != restOfKeyword[i]) {
- return TokenType::Identifier;
+std::shared_ptr<const Token> makeNumberToken(Source &file) {
+ size_t size = 0;
+ char *strlexeme = 0;
+ bool is_float = false;
+
+ while (isDigit(file.stream.peek())) {
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = file.advance();
+ }
+
+ if (file.stream.peek() == '.') {
+ is_float = true;
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = file.advance();
+
+ if (isDigit(file.stream.peek())) {
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = file.advance();
+
+ while (isDigit(file.stream.peek())) {
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = file.advance();
+ }
}
}
- return typeIfMatch;
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = 0;
+
+ return MAKE_TOKEN(TokenType::Number,
+ (is_float ? std::stof(strlexeme) : std::stoi(strlexeme)));
+}
+
+std::shared_ptr<const Token> makeIdentifierToken(Source &file) {
+ size_t size = 0;
+ char *strlexeme = 0;
+
+ while (isAlpha(file.stream.peek()) || isDigit(file.stream.peek())) {
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = file.advance();
+ }
+
+ strlexeme = (char *)realloc(strlexeme, ++size);
+ strlexeme[size - 1] = 0;
+
+ return MAKE_TOKEN(isIdentifierOrKeyword(strlexeme, size), strlexeme);
}
-TokenType Lexer::isIdentifierOrKeywork() {
- switch (*start) {
+TokenType isIdentifierOrKeyword(const char *lexeme, size_t len) {
+ switch (lexeme[0]) {
case 'b':
- return checkKeyword(1, "reak", TokenType::Break);
+ return checkKeyword(lexeme, len, 1, "reak", TokenType::Break);
case 'c':
- if (current - start > 1) {
- switch (*(start + 1)) {
+ if (len > 1) {
+ switch (lexeme[1]) {
case 'l':
- return checkKeyword(2, "ass", TokenType::Class);
+ return checkKeyword(lexeme, len, 2, "ass", TokenType::Break);
case 'o':
- return checkKeyword(2, "ntinue", TokenType::Continue);
+ return checkKeyword(lexeme, len, 2, "ntinue", TokenType::Continue);
}
}
break;
case 'e':
- return checkKeyword(1, "lse", TokenType::Else);
+ return checkKeyword(lexeme, len, 1, "lse", TokenType::Else);
case 'f':
- if (current - start > 1) {
- switch (*(start + 1)) {
+ if (len > 1) {
+ switch (lexeme[1]) {
case 'a':
- return checkKeyword(2, "lse", TokenType::False);
+ return checkKeyword(lexeme, len, 2, "lse", TokenType::False);
case 'o':
- return checkKeyword(2, "r", TokenType::For);
+ return checkKeyword(lexeme, len, 2, "r", TokenType::For);
case 'n':
- return checkKeyword(2, "", TokenType::Fn);
+ return checkKeyword(lexeme, len, 2, "", TokenType::Fn);
}
}
break;
case 'i':
- if (current - start > 1) {
- switch (*(start + 1)) {
+ if (len > 1) {
+ switch (lexeme[1]) {
case 'f':
- return checkKeyword(2, "", TokenType::If);
+ return checkKeyword(lexeme, len, 2, "", TokenType::If);
case 'm':
- return checkKeyword(2, "port", TokenType::Import);
+ return checkKeyword(lexeme, len, 2, "port", TokenType::Import);
}
}
break;
case 'l':
- if (current - start > 1) {
- switch (*(start + 1)) {
+ if (len > 1) {
+ switch (lexeme[1]) {
case 'e':
- return checkKeyword(2, "t", TokenType::Let);
+ return checkKeyword(lexeme, len, 2, "t", TokenType::Let);
case 'o':
- return checkKeyword(2, "op", TokenType::Loop);
+ return checkKeyword(lexeme, len, 2, "op", TokenType::Loop);
}
}
break;
case 'n':
- return checkKeyword(1, "il", TokenType::Nil);
+ return checkKeyword(lexeme, len, 1, "il", TokenType::Nil);
case 'r':
- return checkKeyword(1, "eturn", TokenType::Return);
+ return checkKeyword(lexeme, len, 1, "eturn", TokenType::Return);
case 's':
- return checkKeyword(1, "uper", TokenType::Super);
+ return checkKeyword(lexeme, len, 1, "uper", TokenType::Super);
case 't':
- if (current - start > 1) {
- switch (*(start + 1)) {
+ if (len > 1) {
+ switch (lexeme[1]) {
case 'r':
- return checkKeyword(2, "ue", TokenType::True);
+ return checkKeyword(lexeme, len, 2, "ue", TokenType::True);
case 'h':
- return checkKeyword(2, "is", TokenType::This);
+ return checkKeyword(lexeme, len, 2, "is", TokenType::This);
}
}
break;
case 'w':
- return checkKeyword(1, "hile", TokenType::While);
+ return checkKeyword(lexeme, len, 1, "hile", TokenType::While);
}
return TokenType::Identifier;
}
-std::shared_ptr<const Token> Lexer::makeToken(TokenType type,
- std::string value) {
- if (value != "") {
- return std::make_shared<const Token>(type, value, line,
- current-start, filename);
- }
-
- return std::make_shared<const Token>(type, std::string(start, current), line,
- current-start, filename);
-}
-
-std::shared_ptr<const Token> Lexer::makeNumberToken() {
- while (isDigit(peek())) {
- advance();
- }
-
- if (peek() == '.' && isDigit(peekNext())) {
- advance();
- while (isDigit(peek())) {
- advance();
- }
- }
-
- return makeToken(TokenType::Number);
-}
-
-std::shared_ptr<const Token> Lexer::makeIdentifierToken() {
- while (isAlpha(peek()) || isDigit(peek()) || peek() < 0) {
- advance();
- }
-
- return makeToken(isIdentifierOrKeywork());
-}
-
-std::shared_ptr<const Token> Lexer::makeErrorToken(std::string msg) {
- hadError = true;
- return std::make_shared<const Token>(TokenType::Error, msg, line,
- current-start, filename);
-}
-
-void Lexer::skipWhitespace() {
- do {
- switch (peek()) {
- case '\n':
- line++;
- case ' ':
- case '\r':
- case '\t':
- advance();
- break;
- case '#':
- while (peek() != '\n') {
- advance();
- }
-
- break;
- default:
- return;
- }
- } while (!isAtEnd());
-}
-
-std::shared_ptr<const Token> Lexer::getNextToken() {
- skipWhitespace();
- start = current;
-
- char ch = advance();
-
- if (isDigit(ch)) {
- return makeNumberToken();
- } else if (isAlpha(ch) || (int)ch < 0) {
- return makeIdentifierToken();
- }
-
- switch (ch) {
- case '(':
- return makeToken(TokenType::LeftParen);
- case ')':
- return makeToken(TokenType::RightParen);
- case '{':
- return makeToken(TokenType::LeftBrace);
- case '}':
- return makeToken(TokenType::RightBrace);
- case '?':
- return makeToken(TokenType::Question);
- case ',':
- return makeToken(TokenType::Comma);
- case '.':
- return makeToken(TokenType::Dot);
- case ':':
- return makeToken(TokenType::Colon);
- case ';':
- return makeToken(TokenType::Semicolon);
- case '%':
- return makeToken(TokenType::ModOp);
- case '&':
- if (match('&')) {
- return makeToken(TokenType::And);
- }
- return makeErrorToken("Invalid token '" + std::to_string(*current) + "'.");
- case '|':
- if (match('|')) {
- return makeToken(TokenType::Or);
- }
- return makeErrorToken("Invalid token '" + std::to_string(*current) + "'.");
- case '-':
- return makeToken(TokenType::Minus);
- case '+':
- return makeToken(TokenType::Plus);
- case '/':
- return makeToken(TokenType::Slash);
- case '*':
- return makeToken(TokenType::Star);
- case '!':
- if (match('=')) {
- return makeToken(TokenType::BangEqual);
- }
- return makeToken(TokenType::Bang);
- case '=':
- if (match('=')) {
- return makeToken(TokenType::EqualEqual);
- }
- return makeToken(TokenType::Equal);
- case '>':
- if (match('>')) {
- return makeToken(TokenType::ShiftRight);
- }
- if (match('=')) {
- return makeToken(TokenType::GreaterEqual);
- }
- return makeToken(TokenType::Greater);
- case '<':
- if (match('<')) {
- return makeToken(TokenType::ShiftLeft);
- }
- if (match('=')) {
- return makeToken(TokenType::LessEqual);
- }
- return makeToken(TokenType::Less);
- case '\'': {
- std::string val(1, advance());
- if (!match('\'')) {
- auto res =
- makeErrorToken("A char can't be more then one character long.");
- return res;
- }
- return makeToken(TokenType::Char, val);
- }
- case '"': {
- std::string lexeme;
-
- while (peek() != '"' && !isAtEnd()) {
- if (peek() == '\\') {
- advance();
-
- switch (peek()) {
- case 'n':
- lexeme += "\n";
- break;
- case 't':
- lexeme += "\t";
- break;
- case 'r':
- lexeme += "\r";
- break;
- case '\'':
- lexeme += "'";
- break;
- case '\"':
- lexeme += "\"";
- break;
- case '\\':
- lexeme += "\\";
- break;
- }
-
- advance();
- } else {
- lexeme += advance();
- }
- }
-
- if (isAtEnd()) {
- return makeErrorToken("Unterminated string.");
- }
-
- advance();
- return makeToken(TokenType::String, lexeme);
- }
+TokenType checkKeyword(const char *token, size_t token_len, int startIndex,
+ const std::string_view &restOfKeyword,
+ TokenType typeIfMatch) {
+ if ((token_len - startIndex) != (restOfKeyword.length() + 1)) {
+ return TokenType::Identifier;
}
- if (isAtEnd()) {
- return makeToken(TokenType::Eof, "EoF");
+ if (strcmp(token + startIndex, restOfKeyword.data())) {
+ return TokenType::Identifier;
}
- return makeErrorToken("\033[1;36mHow did you get here?\033[0m");
+ return typeIfMatch;
}
-
-int Lexer::getLine() { return line; }
-int Lexer::getColumn() { return current - start; }
-std::string Lexer::getFilename() { return filename; }
+} // namespace Lexer
#include "token.hpp"
#include "token_type.hpp"
-#include <string>
+#include <cstdint>
+#include <fstream>
#include <memory>
-class Lexer {
-private:
- int line;
- std::string filename;
- const char *start;
- const char *current;
+namespace Lexer {
+struct Source {
+ Source(std::ifstream &file, const char *filepath)
+ : stream(std::move(file)), filepath(filepath), line(1), column(0) {}
+
+ inline char advance() {
+ char res = this->stream.get();
+ ++column;
+ return res;
+ }
public:
- bool hadError;
+ std::ifstream stream;
+ uint64_t line, column;
+ const char *filepath;
+};
-private:
- void skipWhitespace();
- void goToNextLine();
+std::shared_ptr<const Token> getNextToken(Source &file);
- std::shared_ptr<const Token> makeToken(TokenType, std::string = "");
- std::shared_ptr<const Token> makeNumberToken();
- std::shared_ptr<const Token> makeIdentifierToken();
- std::shared_ptr<const Token> makeErrorToken(std::string);
+std::shared_ptr<const Token> makeIdentifierToken(Source &file);
+std::shared_ptr<const Token> makeNumberToken(Source &file);
- TokenType isIdentifierOrKeywork();
- TokenType checkKeyword(int startIndex, const std::string &restOfKeyword,
- TokenType typeIfMatch);
+void skipWhitespace(Source &file);
- char advance();
- char peek() const;
- char peekNext() const;
+TokenType isIdentifierOrKeyword(const char *lexeme, size_t len);
+TokenType checkKeyword(const char *token, size_t token_len, int startIndex,
+ const std::string_view &restOfKeyword,
+ TokenType typeIfMatch);
- bool isAtEnd() const;
- bool isDigit(char ch) const;
- bool isAlpha(char ch) const;
- bool match(char ch);
+inline constexpr bool isDigit(char ch) { return ch >= '0' && ch <= '9'; }
+inline constexpr bool isAlpha(char ch) {
+ return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_' ||
+ ch < -1;
+}
+} // namespace Lexer
-public:
- Lexer(const char *, const std::string &);
+// class Lexer {
+// private:
+// int line;
+// std::string filename;
+// const char *start;
+// const char *current;
- int getLine();
- int getColumn();
- std::string getFilename();
+// public:
+// bool hadError;
- std::shared_ptr<const Token> getNextToken();
-};
+// private:
+// void skipWhitespace();
+// void goToNextLine();
+
+// std::shared_ptr<const Token> makeToken(TokenType, std::string = "");
+// std::shared_ptr<const Token> makeNumberToken();
+// std::shared_ptr<const Token> makeIdentifierToken();
+// std::shared_ptr<const Token> makeErrorToken(std::string);
+
+// TokenType isIdentifierOrKeywork();
+// TokenType checkKeyword(int startIndex, const std::string &restOfKeyword,
+// TokenType typeIfMatch);
+
+// char advance();
+// char peek() const;
+// char peekNext() const;
+
+// bool isAtEnd() const;
+// bool isDigit(char ch) const;
+// bool isAlpha(char ch) const;
+// bool match(char ch);
+
+// public:
+// Lexer(std::ifstream &, const std::string &);
+
+// int getLine();
+// int getColumn();
+// std::string getFilename();
+
+// std::shared_ptr<const Token> getNextToken();
+// };
#endif
-#include <fcntl.h>
+#include <fstream>
#include <iostream>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "parser.hpp"
#include "interpreter.hpp"
return 1;
}
- int fd = open(argv[1], O_RDONLY, S_IRUSR | S_IWUSR);
- struct stat sb;
-
- if (fstat(fd, &sb) == -1) {
- perror("Couldn't get file size.\n");
+ std::ifstream file(argv[1]);
+ if (!file.good()) {
+ std::cerr << "I/O error: couldn't load file `" << argv[1] << "`.";
+ return 1;
}
- char *file_in_memory =
- (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-
- Parser *parser = new Parser(file_in_memory, argv[1]);
+ Parser *parser = new Parser(file, argv[1]);
std::vector<std::unique_ptr<Stmt>> statements = parser->parse();
// AST_Printer test;
}
delete parser;
- munmap(file_in_memory, sb.st_size);
- close(fd);
}
#include "parser.hpp"
#include "syntax_error.hpp"
+#include "token_type.hpp"
#include <fcntl.h>
+#include <fstream>
#include <iostream>
+#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
std::vector<std::unique_ptr<Stmt>> Parser::importStmt() {
std::shared_ptr<const Token> path =
consume("Expected path to file to import but instead got: '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::String);
- if (importedFiles.contains(path->lexeme)) {
+ const char *filepath = std::get<const char *>(path->lexeme);
+ if (importedFiles.contains(filepath)) {
throw SyntaxError(previous.get(),
- "Recursive file import: '" + path->lexeme +
+ "Recursive file import: '" + std::string(filepath) +
"' has already been imported or is the main file.");
}
- importedFiles.insert(path->lexeme);
- int fd = open(path->lexeme.c_str(), O_RDONLY, S_IRUSR | S_IWUSR);
- struct stat sb;
-
- if (fstat(fd, &sb) == -1) {
- perror("Couldn't get file size.\n");
- }
-
- char *sourceFile =
- (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-
- CONSUME_SEMICOLON("import");
-
- return (new Parser(sourceFile, path->lexeme, importedFiles))->parse();
+ importedFiles.insert(filepath);
+ std::ifstream sourceFile(filepath);
CONSUME_SEMICOLON("import");
- return (new Parser(sourceFile, path->lexeme, importedFiles))->parse();
+ return (new Parser(sourceFile, filepath, importedFiles))->parse();
}
std::unique_ptr<VarStmt> Parser::varDecl() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
std::shared_ptr<const Token> name =
consume("Expected variable name after 'let' keyword but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::Identifier);
std::unique_ptr<Expr> value;
}
consume("Expected ';' at the end of a statement but instead got: '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::Semicolon);
- return std::make_unique<VarStmt>(line, col, lexer->getFilename(), name,
- value);
+ return std::make_unique<VarStmt>(line, col, filename, name, value);
}
std::unique_ptr<FnStmt> Parser::functionDecl(const std::string &kind) {
std::vector<std::shared_ptr<const Token>> args;
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
+
std::shared_ptr<const Token> name =
consume("Expected " + kind + " name.", TokenType::Identifier);
consume("Expected argument list after trait function name but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::LeftParen);
if (!check(TokenType::RightParen)) {
do {
std::shared_ptr<const Token> arg = consume(
"Expected name of trait function parameter but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::Identifier);
args.push_back(arg);
} while (match(TokenType::Comma));
}
- consume("Expected ')' but instead got '" + current->lexeme + "'.",
+
+ consume("Expected ')' but instead got '" + type2str(current->type) + "'.",
TokenType::RightParen);
consume("Expected '{' after function signature but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::LeftBrace);
- return std::make_unique<FnStmt>(line, col, lexer->getFilename(), name, args,
+ return std::make_unique<FnStmt>(line, col, filename, name, args,
stmtSequence());
}
std::unique_ptr<ClassStmt> Parser::classDecl() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
- std::shared_ptr<const Token> name =
- consume("Expected class name but instead got '" + current->lexeme + "'.",
- TokenType::Identifier);
+ std::shared_ptr<const Token> name = consume(
+ "Expected class name but instead got '" + type2str(current->type) + "'.",
+ TokenType::Identifier);
std::unique_ptr<VariableExpr> superclass;
if (match(TokenType::Colon)) {
std::shared_ptr<const Token> supername =
consume("Expected superclass name.", TokenType::Identifier);
- superclass = std::make_unique<VariableExpr>(line, col, lexer->getFilename(),
- supername);
+ superclass = std::make_unique<VariableExpr>(line, col, filename, supername);
}
- auto clas = std::make_unique<ClassStmt>(line, col, lexer->getFilename(), name,
- superclass,
+ auto clas = std::make_unique<ClassStmt>(line, col, filename, name, superclass,
std::vector<std::unique_ptr<Stmt>>());
if (match(TokenType::Semicolon)) {
return clas;
throw SyntaxError(current.get(),
"Expected either a semicolon for an inline class "
"definition or a sequence of methods but instead got '" +
- current->lexeme + "'.");
+ type2str(current->type) + "'.");
}
}
}
std::unique_ptr<Stmt> Parser::scopedStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
- return std::make_unique<ScopedStmt>(line, col, lexer->getFilename(),
- stmtSequence());
+ return std::make_unique<ScopedStmt>(current->line, current->column,
+ current->filename, stmtSequence());
}
std::unique_ptr<Stmt> Parser::ifStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
+
std::unique_ptr<Expr> cond = expression();
std::unique_ptr<Stmt> trueBranch = statement();
std::unique_ptr<Stmt> falseBranch;
falseBranch = statement();
}
- return std::make_unique<IfStmt>(line, col, lexer->getFilename(), cond,
- trueBranch, falseBranch);
+ return std::make_unique<IfStmt>(line, col, filename, cond, trueBranch,
+ falseBranch);
}
std::unique_ptr<Stmt> Parser::whileStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
auto condition = expression();
auto body = statement();
- return std::make_unique<WhileStmt>(line, col, lexer->getFilename(), condition,
- body);
+ return std::make_unique<WhileStmt>(line, col, filename, condition, body);
}
std::unique_ptr<Stmt> Parser::loopStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
std::unique_ptr<Expr> cond = std::make_unique<LiteralExpr>(
- line, col, lexer->getFilename(),
+ line, col, filename,
std::make_shared<const Token>(TokenType::True, "true", previous->line,
previous->column, previous->filename));
- return std::make_unique<WhileStmt>(line, col, lexer->getFilename(), cond,
- statement());
+ return std::make_unique<WhileStmt>(line, col, filename, cond, statement());
}
std::unique_ptr<Stmt> Parser::forStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
consume("Expected '(' at the beginning of for statement but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::LeftParen);
std::unique_ptr<Stmt> initializer;
cond = expression();
}
consume("Expected ';' after loop condition but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::Semicolon);
std::unique_ptr<Expr> increment;
increment = expression();
}
consume("Expected ')' after loop clauses but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::RightParen);
std::unique_ptr<Stmt> body = statement();
if (!cond) {
cond = std::make_unique<LiteralExpr>(
- line, col, lexer->getFilename(),
+ line, col, filename,
std::make_shared<const Token>(TokenType::True, "true", previous->line,
previous->column, previous->filename));
}
- return std::make_unique<ForStmt>(line, col, lexer->getFilename(), initializer,
- cond, increment, body);
+ return std::make_unique<ForStmt>(line, col, filename, initializer, cond,
+ increment, body);
}
std::unique_ptr<Stmt> Parser::returnStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
std::unique_ptr<Expr> value;
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
if (!check(TokenType::Semicolon)) {
value = expression();
}
CONSUME_SEMICOLON("return");
- return std::make_unique<ReturnStmt>(line, col, lexer->getFilename(), value);
+ return std::make_unique<ReturnStmt>(line, col, filename, value);
}
std::unique_ptr<Stmt> Parser::expressionStmt() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
- auto expr =
- std::make_unique<ExprStmt>(line, col, lexer->getFilename(), expression());
+ auto expr = std::make_unique<ExprStmt>(line, col, filename, expression());
CONSUME_SEMICOLON("expression");
return expr;
std::unique_ptr<Expr> Parser::expression() {
if (match(TokenType::Break)) {
- int line = lexer->getLine(), col = lexer->getColumn();
- return std::make_unique<BreakExpr>(line, col, lexer->getFilename());
+ return std::make_unique<BreakExpr>(current->line, current->column,
+ current->filename);
} else if (match(TokenType::Break)) {
- int line = lexer->getLine(), col = lexer->getColumn();
- return std::make_unique<ContinueExpr>(line, col, lexer->getFilename());
+ return std::make_unique<ContinueExpr>(current->line, current->column,
+ current->filename);
}
return assignment();
std::unique_ptr<Expr> Parser::assignment() {
std::unique_ptr<Expr> left = orExpr();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
if (match(TokenType::Equal)) {
std::unique_ptr<Expr> value = assignment();
if (auto var = dynamic_cast<VariableExpr *>(left.get())) {
- return std::make_unique<AssignExpr>(line, col, lexer->getFilename(),
- var->variable, value);
+ return std::make_unique<AssignExpr>(line, col, filename, var->variable,
+ value);
} else if (auto get = dynamic_cast<GetFieldExpr *>(left.get())) {
- return std::make_unique<SetFieldExpr>(line, col, lexer->getFilename(),
- get->instance, get->field, value);
+ return std::make_unique<SetFieldExpr>(line, col, filename, get->instance,
+ get->field, value);
}
throw SyntaxError(value.get(), "Invalid assignment value.");
} else if (match(TokenType::Question)) {
std::unique_ptr<Expr> trueExpr = assignment();
consume("Expected ':' between ternary expressions but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::Colon);
- return std::make_unique<TernaryExpr>(line, col, lexer->getFilename(), left,
- trueExpr, assignment());
+ return std::make_unique<TernaryExpr>(line, col, filename, left, trueExpr,
+ assignment());
}
return left;
std::unique_ptr<Expr> Parser::orExpr() {
std::unique_ptr<Expr> left = andExpr();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (match(TokenType::Or)) {
std::shared_ptr<const Token> op = previous;
- left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
- andExpr(), op);
+ left =
+ std::make_unique<BinaryExpr>(line, col, filename, left, andExpr(), op);
}
return left;
std::unique_ptr<Expr> Parser::andExpr() {
std::unique_ptr<Expr> left = equality();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (match(TokenType::And)) {
std::shared_ptr<const Token> op = previous;
- left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
- equality(), op);
+ left =
+ std::make_unique<BinaryExpr>(line, col, filename, left, equality(), op);
}
return left;
std::unique_ptr<Expr> Parser::equality() {
std::unique_ptr<Expr> left = comparison();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (match(TokenType::EqualEqual, TokenType::BangEqual)) {
std::shared_ptr<const Token> op = previous;
- left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
- comparison(), op);
+ left = std::make_unique<BinaryExpr>(line, col, filename, left, comparison(),
+ op);
}
return left;
std::unique_ptr<Expr> Parser::comparison() {
std::unique_ptr<Expr> left = term();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (match(TokenType::Greater, TokenType::GreaterEqual, TokenType::Less,
TokenType::LessEqual)) {
std::shared_ptr<const Token> op = previous;
- left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
- term(), op);
+ left = std::make_unique<BinaryExpr>(line, col, filename, left, term(), op);
}
return left;
std::unique_ptr<Expr> Parser::term() {
std::unique_ptr<Expr> left = factor();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (match(TokenType::Plus, TokenType::Minus, TokenType::ModOp)) {
std::shared_ptr<const Token> op = previous;
- left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
- factor(), op);
+ left =
+ std::make_unique<BinaryExpr>(line, col, filename, left, factor(), op);
}
return left;
std::unique_ptr<Expr> Parser::factor() {
std::unique_ptr<Expr> left = unary();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (match(TokenType::Star, TokenType::Slash)) {
std::shared_ptr<const Token> op = previous;
- left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
- unary(), op);
+ left = std::make_unique<BinaryExpr>(line, col, filename, left, unary(), op);
}
return left;
}
std::unique_ptr<Expr> Parser::unary() {
- int line = lexer->getLine(), col = lexer->getColumn();
if (match(TokenType::Bang, TokenType::Minus)) {
std::shared_ptr<const Token> op = previous;
- return std::make_unique<UnaryExpr>(line, col, lexer->getFilename(), unary(),
- op);
+ return std::make_unique<UnaryExpr>(current->line, current->column,
+ current->filename, unary(), op);
+ } else {
+ return call();
}
-
- return call();
}
std::unique_ptr<Expr> Parser::call() {
std::unique_ptr<Expr> expr = primary();
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
while (1) {
if (match(TokenType::LeftParen)) {
}
consume("Expected ')' after function call but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::RightParen);
- expr = std::make_unique<FnCallExpr>(line, col, lexer->getFilename(), expr,
- args);
+ expr = std::make_unique<FnCallExpr>(line, col, filename, expr, args);
} else if (match(TokenType::Dot)) {
std::shared_ptr<const Token> prop =
consume("Expected class property or method but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::Identifier);
- expr = std::make_unique<GetFieldExpr>(line, col, lexer->getFilename(),
- expr, prop);
+ expr = std::make_unique<GetFieldExpr>(line, col, filename, expr, prop);
} else {
break;
}
}
std::unique_ptr<Expr> Parser::primary() {
- int line = lexer->getLine(), col = lexer->getColumn();
+ int line = current->line, col = current->column;
+ const char *filename = current->filename;
if (match(TokenType::Super)) {
consume("Expected '.' after super keyword.", TokenType::Dot);
std::shared_ptr<const Token> field =
consume("Expected superclass field.", TokenType::Identifier);
- return std::make_unique<SuperExpr>(line, col, lexer->getFilename(), field);
+ return std::make_unique<SuperExpr>(line, col, filename, field);
} else if (match(TokenType::This)) {
- return std::make_unique<ThisExpr>(line, col, lexer->getFilename(),
- previous);
+ return std::make_unique<ThisExpr>(line, col, filename, previous);
} else if (match(TokenType::Identifier)) {
- return std::make_unique<VariableExpr>(line, col, lexer->getFilename(),
- previous);
+ return std::make_unique<VariableExpr>(line, col, filename, previous);
} else if (match(TokenType::Number, TokenType::Char, TokenType::String,
TokenType::True, TokenType::False, TokenType::Nil)) {
- return std::make_unique<LiteralExpr>(line, col, lexer->getFilename(),
- previous);
+ return std::make_unique<LiteralExpr>(line, col, filename, previous);
} else if (match(TokenType::LeftParen)) {
std::unique_ptr<Expr> expr = expression();
consume("Missing closing parenthesis ')' after expression.",
TokenType::RightParen);
- return std::make_unique<GroupingExpr>(line, col, lexer->getFilename(),
- expr);
+ return std::make_unique<GroupingExpr>(line, col, filename, expr);
}
- throw SyntaxError(current.get(), "'" + current->lexeme +
+ throw SyntaxError(current.get(), "'" + type2str(current->type) +
"' not a valid expression statement.");
}
}
}
-bool Parser::isAtEnd() { return current->type == TokenType::Eof; }
-
-template <typename... TokenTypes>
-bool Parser::check(const TokenTypes &...types) {
- return ((current->type == types) || ...);
-}
-
-template <typename... TokenTypes>
-bool Parser::match(const TokenTypes &...types) {
- if (!(check(types) || ...)) {
- return false;
- }
-
- advance();
- return true;
-}
-
std::shared_ptr<const Token> Parser::advance() {
previous = current;
return current;
}
- current = lexer->getNextToken();
- if (lexer->hadError) {
- throw SyntaxError(current.get(), current->lexeme);
- }
- return previous;
-}
-
-template <typename... TokenTypes>
-std::shared_ptr<const Token> Parser::consume(const std::string &msg,
- const TokenTypes &...types) {
- if (!(check(types) || ...)) {
- throw SyntaxError(current.get(), msg);
+ current = Lexer::getNextToken(this->source);
+ if (current->type == TokenType::Error) {
+ throw SyntaxError(current.get(), std::get<const char *>(current->lexeme));
+ } else {
+ return previous;
}
-
- std::shared_ptr<const Token> res = current;
- advance();
- return res;
}
std::vector<std::unique_ptr<Stmt>> Parser::stmtSequence() {
}
consume("Expected '}' at scope block end but instead got '" +
- current->lexeme + "'.",
+ type2str(current->type) + "'.",
TokenType::RightBrace);
return stmts;
}
+
+template <typename... TokenTypes>
+bool Parser::match(const TokenTypes &...types) {
+ if (!(check(types) || ...)) {
+ return false;
+ }
+
+ advance();
+ return true;
+}
+
+std::shared_ptr<const Token> Parser::consume(const std::string &msg,
+ const TokenType &expected) {
+ if (!check(expected)) {
+ throw SyntaxError(current.get(), msg);
+ }
+
+ std::shared_ptr<const Token> res = current;
+ advance();
+ return res;
+}
#include "expressions.hpp"
#include "lexer.hpp"
#include "statements.hpp"
+#include "syntax_error.hpp"
+#include "token_type.hpp"
+#include <fstream>
#include <memory>
#include <unordered_set>
#include <vector>
TokenType::Semicolon);
class Parser {
-private:
- Lexer *lexer;
- std::shared_ptr<const Token> current;
- std::shared_ptr<const Token> previous;
- std::unordered_set<std::string> importedFiles;
-
public:
- bool hadError;
+ Parser(std::ifstream &file, const char *filename)
+ : source(Lexer::Source(file, filename)),
+ current(Lexer::getNextToken(this->source)), previous(current) {
+ importedFiles.insert(filename);
+ }
+
+ Parser(std::ifstream &file, const char *filename,
+ std::unordered_set<std::string> &importedFiles)
+ : importedFiles(importedFiles), source(Lexer::Source(file, filename)),
+ current(Lexer::getNextToken(this->source)), previous(current) {}
+
+ std::vector<std::unique_ptr<Stmt>> parse();
private:
- bool isAtEnd();
void synchronize();
std::shared_ptr<const Token> advance();
- template <typename... TokenTypes> bool check(const TokenTypes &...);
+ inline bool isAtEnd() { return current->type == TokenType::Eof; }
+
template <typename... TokenTypes>
- std::shared_ptr<const Token> consume(const std::string &msg, const TokenTypes &...);
- template <typename... TokenTypes> bool match(const TokenTypes &...);
+ inline bool check(const TokenTypes &...types) {
+ return ((current->type == types) || ...);
+ }
+
+ template <typename... TokenTypes> bool match(const TokenTypes &...types);
+ std::shared_ptr<const Token> consume(const std::string &msg,
+ const TokenType &expected);
std::vector<std::unique_ptr<Stmt>> importStmt();
std::unique_ptr<FnStmt> functionDecl(const std::string &);
std::unique_ptr<Expr> call();
std::unique_ptr<Expr> primary();
-public:
- Parser(const char *file, const std::string &filename)
- : lexer(new Lexer(file, filename)), current(lexer->getNextToken()),
- previous(current) {
- importedFiles.insert(filename);
- }
-
- Parser(const char *file, const std::string &filename,
- std::unordered_set<std::string> &importedFiles)
- : importedFiles(importedFiles), lexer(new Lexer(file, filename)),
- current(lexer->getNextToken()), previous(current) {}
+private:
+ Lexer::Source source;
+ std::shared_ptr<const Token> current;
+ std::shared_ptr<const Token> previous;
+ std::unordered_set<std::string> importedFiles;
- std::vector<std::unique_ptr<Stmt>> parse();
+public:
+ bool hadError;
};
#endif
#include "resolver.hpp"
#include "syntax_error.hpp"
+#include <string_view>
void Resolver::resolve(std::vector<std::unique_ptr<Stmt>> &stmts) {
for (auto &&stmt : stmts) {
return;
}
+ auto namestr = std::get<const char *>(name->lexeme);
std::map<std::string, VarState> &scope = scopes.back();
- if (scope.contains(name->lexeme)) {
+ if (scope.contains(namestr)) {
throw SyntaxError(name, "Variable with this name already exists.");
}
- scope.insert(std::make_pair(name->lexeme, VarState::Init));
+ scope.insert(std::make_pair(namestr, VarState::Init));
}
void Resolver::define(const Token *name) {
return;
}
- scopes.back().insert_or_assign(name->lexeme, VarState::Ready);
+ scopes.back().insert_or_assign(std::get<const char *>(name->lexeme),
+ VarState::Ready);
}
void Resolver::resolveLocal(Expr *expr, const Token *name) {
for (int i = scopes.size() - 1; i >= 0; i--) {
- if (scopes[i].contains(name->lexeme)) {
+ if (scopes[i].contains(std::get<const char *>(name->lexeme))) {
interpreter->resolve(expr, scopes.size() - 1 - i);
return;
}
for (auto &&stmt : clas->body) {
auto method = dynamic_cast<FnStmt *>(stmt.get());
- if (method && method->name->lexeme == "init") {
+ if (method && std::string_view(
+ std::get<const char *>(method->name->lexeme)) == "init") {
resolveFunction(method, FunctionType::Initializer);
} else {
resolveFunction(method, FunctionType::Function);
}
Value Resolver::visitVarExpr(VariableExpr *expr) {
- if (!scopes.empty() && scopes.back().contains(expr->variable->lexeme) &&
- scopes.back().find(expr->variable->lexeme)->second != VarState::Ready) {
+ if (!scopes.empty() &&
+ scopes.back().contains(std::get<const char *>(expr->variable->lexeme)) &&
+ scopes.back()
+ .find(std::get<const char *>(expr->variable->lexeme))
+ ->second != VarState::Ready) {
throw SyntaxError(expr, "You are trying to read the value of a variable "
"that hasn't been defined yet.");
}
--- /dev/null
+#include "token.hpp"
+#include <iomanip>
+
+std::ostream &operator<<(std::ostream &os, const Token tk) {
+ os << "Token { line: " << tk.line << ", column: " << tk.column
+ << ", filename: " << tk.filename << ", type: " << type2str(tk.type);
+ if (!std::holds_alternative<std::nullptr_t>(tk.lexeme)) {
+ os << ", lexeme: ";
+ std::visit(
+ [&](auto &&arg) {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (std::is_same_v<T, int32_t> ||
+ std::is_same_v<T, float> || std::is_same_v<T, char>) {
+ os << arg;
+ } else if constexpr (std::is_same_v<T, const char *>) {
+ os << std::quoted(arg);
+ }
+ },
+ tk.lexeme);
+ }
+
+ os << " }";
+ return os;
+}
#include "token_type.hpp"
-#include <string>
+#include <cstddef>
+#include <cstdint>
+#include <ostream>
+#include <variant>
+
+using literal_t =
+ std::variant<const char *, char, int32_t, float, std::nullptr_t>;
+
+struct Token {
+ Token(TokenType type, literal_t lexeme, int32_t line, int32_t column,
+ const char *filename)
+ : type(type), lexeme(lexeme), line(line), column(column),
+ filename(filename) {}
-class Token {
public:
- int line;
- int column;
+ int32_t line;
+ int32_t column;
TokenType type;
- std::string lexeme;
- std::string filename;
-
- Token(TokenType type, const std::string &lexeme, int line, int column, const std::string &filename)
- : type(type), lexeme(lexeme), line(line), column(column), filename(filename) {}
+ literal_t lexeme;
+ const char *filename;
};
+std::ostream &operator<<(std::ostream &os, const Token tk);
+
#endif
#ifndef TOKEN_TYPE_H
#define TOKEN_TYPE_H
-enum TokenType {
+#include <algorithm>
+#include <array>
+#include <iostream>
+#include <string>
+#include <utility>
+
+enum class TokenType {
// Single-character tokens.
LeftParen,
RightParen,
Error,
};
+static constexpr std::array<std::pair<TokenType, const char *>, 50>
+ type2str_map = {{
+ {TokenType::LeftParen, "("},
+ {TokenType::RightParen, ")"},
+ {TokenType::LeftBrace, "{"},
+ {TokenType::RightBrace, "}"},
+ {TokenType::Question, "?"},
+ {TokenType::Comma, ","},
+ {TokenType::Dot, "."},
+ {TokenType::Colon, ","},
+ {TokenType::Semicolon, ";"},
+ {TokenType::ModOp, "%"},
+ {TokenType::Minus, "-"},
+ {TokenType::Plus, "+"},
+ {TokenType::Slash, "/"},
+ {TokenType::Star, "*"},
+
+ {TokenType::Bang, "!"},
+ {TokenType::BangEqual, "!="},
+ {TokenType::Equal, "="},
+ {TokenType::EqualEqual, "=="},
+ {TokenType::ShiftRight, ">>"},
+ {TokenType::Greater, ">"},
+ {TokenType::GreaterEqual, ">="},
+ {TokenType::ShiftLeft, "<<"},
+ {TokenType::Less, "<"},
+ {TokenType::LessEqual, "<="},
+
+ {TokenType::Identifier, "Identifier"},
+ {TokenType::String, "String"},
+ {TokenType::Char, "Char"},
+ {TokenType::Number, "Number"},
+
+ {TokenType::Import, "import"},
+ {TokenType::Let, "let"},
+ {TokenType::And, "and"},
+ {TokenType::Class, "class"},
+ {TokenType::Else, "else"},
+ {TokenType::False, "false"},
+ {TokenType::Fn, "fn"},
+ {TokenType::For, "for"},
+ {TokenType::If, "if"},
+ {TokenType::Nil, "nil"},
+ {TokenType::Or, "or"},
+ {TokenType::Return, "return"},
+ {TokenType::Break, "break"},
+ {TokenType::Continue, "continue"},
+ {TokenType::Super, "super"},
+ {TokenType::This, "this"},
+ {TokenType::True, "true"},
+ {TokenType::While, "while"},
+ {TokenType::Loop, "loop"},
+ {TokenType::Main, "main"},
+ {TokenType::Eof, "eof"},
+ {TokenType::Error, "error"},
+ }};
+
+inline std::string type2str(TokenType type) {
+ const auto it =
+ std::find_if(type2str_map.begin(), type2str_map.end(),
+ [&](const auto &value) { return value.first == type; });
+
+ if (it != type2str_map.end()) {
+ return it->second;
+ } else {
+ __builtin_unreachable();
+ }
+}
+
#endif