--- /dev/null
+build/
+.cache/
\ No newline at end of file
--- /dev/null
+cmake_minimum_required (VERSION 3.26.4)
+
+add_compile_options(-O2)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+project (lbpl VERSION 0.1.0)
+
+include_directories(lib)
+
+file(GLOB SRCFILES src/*.cpp)
+add_executable(${PROJECT_NAME} ${SRCFILES})
--- /dev/null
+* Running it
+#+begin_src
+git clone https://github.com/LeonardoBizzoni/LBPL.git
+cd LBPL
+mkdir build
+cd build
+cmake ..
+make
+#+end_src
+
+* Example script
+#+begin_src
+fn fib(n) {
+ if (n < 2) {
+ return n;
+ }
+ return fib(n-1)+fib(n-2);
+}
+
+class Prova {
+ init(n) {
+ let start = clock();
+ fib(n);
+ let end = clock();
+
+ println("fib(26) runtime: " + (end-start));
+ }
+
+ prova() { println("prova"); }
+}
+
+let a = Prova(25);
+a.var1 = 10;
+println(a.var1);
+a.var1 = 210;
+println(a.var1);
+#+end_src
--- /dev/null
+#ifndef LBPL_CALLABLE_H
+#define LBPL_CALLABLE_H
+
+#include "LBPLTypes.h"
+#include <vector>
+
+class Interpreter;
+
+class LBPLCallable {
+public:
+ virtual int arity() = 0;
+ virtual LBPLType call(Interpreter*, std::vector<LBPLType>&) = 0;
+};
+
+#endif
--- /dev/null
+#ifndef LBPL_CLASS_H
+#define LBPL_CLASS_H
+
+#include "LBPLCallable.h"
+#include "LBPLFunction.h"
+
+#include <map>
+#include <memory>
+
+class LBPLClass : public LBPLCallable {
+public:
+ std::string name;
+ std::shared_ptr<LBPLClass> superclass;
+ std::map<std::string, LBPLFunc *> methods;
+
+public:
+ LBPLClass(const std::string &name, std::shared_ptr<LBPLClass> &superclass,
+ std::map<std::string, LBPLFunc *> &methods)
+ : name(name), superclass(superclass), methods(methods) {}
+ LBPLClass(const std::string &name, std::map<std::string, LBPLFunc *> &methods)
+ : name(name), superclass(nullptr), methods(methods) {}
+
+ LBPLFunc *findMethod(const std::string &);
+
+ int arity() override;
+ LBPLType call(Interpreter *, std::vector<LBPLType> &) override;
+};
+
+#endif
--- /dev/null
+#ifndef LBPL_FUNCTION_H
+#define LBPL_FUNCTION_H
+
+#include "LBPLCallable.h"
+#include "environment.h"
+#include "statements.h"
+#include <memory>
+
+class LBPLFunc : public LBPLCallable {
+private:
+ FnStmt *stmt;
+ std::shared_ptr<Environment> closureEnv;
+ bool isInitializer;
+
+public:
+ LBPLFunc(FnStmt *stmt, std::shared_ptr<Environment> &closureEnv,
+ bool isInitializer)
+ : stmt(stmt), closureEnv(closureEnv), isInitializer(isInitializer) {}
+ LBPLFunc(FnStmt *stmt, std::shared_ptr<Environment> &&closureEnv,
+ bool isInitializer)
+ : stmt(stmt), closureEnv(closureEnv), isInitializer(isInitializer) {}
+
+ void bind(std::shared_ptr<LBPLInstance> &&instance);
+ void bind(std::shared_ptr<LBPLInstance> &instance);
+
+ int arity() override;
+ LBPLType call(Interpreter *, std::vector<LBPLType> &) override;
+};
+
+#endif
--- /dev/null
+#ifndef LBPL_INSTANCE_H
+#define LBPL_INSTANCE_H
+
+#include "LBPLClass.h"
+#include "runtime_error.h"
+
+#include <map>
+#include <memory>
+
+class LBPLInstance {
+private:
+ LBPLClass *lbplClass;
+ std::map<std::string, LBPLType> fields;
+
+public:
+ LBPLInstance(LBPLClass *lbplClass) : lbplClass(lbplClass), fields() {}
+ LBPLInstance(LBPLInstance *other)
+ : lbplClass(other->lbplClass), fields(other->fields) {}
+
+ LBPLType get(const Token *name);
+ void set(const Token *name, LBPLType &value);
+};
+
+#endif
--- /dev/null
+#ifndef LBPL_TYPES_H
+#define LBPL_TYPES_H
+
+#include <memory>
+#include <string>
+#include <variant>
+
+class LBPLInstance;
+class LBPLCallable;
+class LBPLClass;
+
+using LBPLType =
+ std::variant<std::string, int, double, bool, char, std::nullptr_t,
+ std::shared_ptr<LBPLClass>, std::shared_ptr<LBPLInstance>,
+ std::shared_ptr<LBPLCallable>>;
+#endif
--- /dev/null
+#ifndef AST_PRINTER_H
+#define AST_PRINTER_H
+
+#include "tree_nodes.h"
+#include "visitor.h"
+
+class AST_Printer : public Statement::Visitor, public Expression::Visitor {
+ void printLocation(Stmt *);
+
+ void visitFnStmt(FnStmt *) override;
+ void visitVarStmt(VarStmt *) override;
+ void visitClassStmt(ClassStmt *) override;
+ void visitIfStmt(IfStmt *) override;
+ void visitWhileStmt(WhileStmt *) override;
+ void visitForStmt(ForStmt *) override;
+ void visitScopedStmt(ScopedStmt *) override;
+ void visitExprStmt(ExprStmt *) override;
+ void visitReturnStmt(ReturnStmt *) override;
+
+ LBPLType visitBinaryExpr(BinaryExpr *) override;
+ LBPLType visitBreakExpr(BreakExpr *) override;
+ LBPLType visitContinueExpr(ContinueExpr *) override;
+ LBPLType visitUnaryExpr(UnaryExpr *) override;
+ LBPLType visitLiteralExpr(LiteralExpr *) override;
+ LBPLType visitGroupExpr(GroupingExpr *) override;
+ LBPLType visitSuperExpr(SuperExpr *) override;
+ LBPLType visitThisExpr(ThisExpr *) override;
+ LBPLType visitCallExpr(FnCallExpr *) override;
+ LBPLType visitGetFieldExpr(GetFieldExpr *) override;
+ LBPLType visitSetFieldExpr(SetFieldExpr *) override;
+ LBPLType visitTernaryExpr(TernaryExpr *) override;
+ LBPLType visitVarExpr(VariableExpr *) override;
+ LBPLType visitAssignExpr(AssignExpr *) override;
+};
+
+#endif
--- /dev/null
+#ifndef BUILTIN_METHODS_H
+#define BUILTIN_METHODS_H
+
+#include "LBPLCallable.h"
+#include "statements.h"
+
+#include <chrono>
+#include <iostream>
+#include <variant>
+
+class LBPLPrintln : public LBPLCallable {
+public:
+ LBPLPrintln() {}
+
+ int arity() override { return 1; };
+
+ LBPLType call(Interpreter *, std::vector<LBPLType> &args) override {
+ if (std::holds_alternative<std::string>(args[0])) {
+ std::cout << std::get<std::string>(args[0]) << std::endl;
+ } else if (std::holds_alternative<int>(args[0])) {
+ std::cout << std::get<int>(args[0]) << std::endl;
+ } else if (std::holds_alternative<double>(args[0])) {
+ std::cout << std::get<double>(args[0]) << std::endl;
+ } else if (std::holds_alternative<char>(args[0])) {
+ std::cout << std::get<char>(args[0]) << std::endl;
+ } else if (std::holds_alternative<bool>(args[0])) {
+ std::cout << (std::get<bool>(args[0]) ? "true" : "false") << std::endl;
+ }
+
+ return nullptr;
+ }
+};
+
+class LBPLClock : public LBPLCallable {
+public:
+ LBPLClock() {}
+
+ int arity() override { return 0; };
+
+ LBPLType call(Interpreter *, std::vector<LBPLType> &args) override {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count() /
+ 1000.0;
+ }
+};
+
+#endif
--- /dev/null
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "token.h"
+#include "token_type.h"
+
+#include <string>
+#include <iostream>
+
+#endif
--- /dev/null
+#ifndef ENVIRONMENT_H
+#define ENVIRONMENT_H
+
+#include "LBPLTypes.h"
+#include "token.h"
+#include "tree_nodes.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <variant>
+
+class Environment {
+public:
+ std::map<std::string, LBPLType> env;
+ std::shared_ptr<Environment> enclosing;
+
+public:
+ Environment() : enclosing(nullptr) {}
+ Environment(Environment &other)
+ : env(other.env),
+ enclosing(other.enclosing ? other.enclosing->clone() : nullptr) {}
+ Environment(std::shared_ptr<Environment> &enclosing) : enclosing(enclosing) {}
+
+ std::shared_ptr<Environment> clone() {
+ return std::make_shared<Environment>(*this);
+ }
+
+ void define(const std::string &, LBPLType &);
+ void define(const std::string &, LBPLType &&);
+
+ void printEnv(const std::string &&);
+
+ LBPLType get(std::shared_ptr<const Token> &);
+ LBPLType getAt(int, std::shared_ptr<const Token> &);
+ LBPLType getAt(int, const std::string &);
+ void assign(std::shared_ptr<const Token> &, LBPLType &);
+ void assign(std::shared_ptr<const Token> &, LBPLType &&);
+ void assignAt(int, std::shared_ptr<const Token> &, LBPLType &);
+};
+
+#endif
--- /dev/null
+#ifndef EXPRESSIONS_H
+#define EXPRESSIONS_H
+
+#include "common.h"
+#include "visitor.h"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+struct Expr {
+ std::string file;
+ int line, column;
+
+ Expr(int line, int column, const std::string &filename)
+ : line(line), column(column), file(filename) {}
+
+ virtual ~Expr(){};
+ virtual LBPLType accept(Expression::Visitor *) { return nullptr; }
+};
+
+struct BinaryExpr : public Expr {
+ std::unique_ptr<Expr> left;
+ std::unique_ptr<Expr> right;
+ std::shared_ptr<const Token> op;
+
+ BinaryExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &left, std::unique_ptr<Expr> &right,
+ std::shared_ptr<const Token> &op)
+ : left(std::move(left)), right(std::move(right)), op(op),
+ Expr(line, column, file) {}
+ BinaryExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &left, std::unique_ptr<Expr> &&right,
+ std::shared_ptr<const Token> &op)
+ : left(std::move(left)), right(std::move(right)), op(op),
+ Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitBinaryExpr(this);
+ }
+};
+
+struct BreakExpr : public Expr {
+ BreakExpr(int line, int column, const std::string &file)
+ : Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitBreakExpr(this);
+ }
+};
+
+struct ContinueExpr : public Expr {
+ ContinueExpr(int line, int column, const std::string &file)
+ : Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitContinueExpr(this);
+ }
+};
+
+struct UnaryExpr : public Expr {
+ std::unique_ptr<Expr> right;
+ std::shared_ptr<const Token> op;
+
+ UnaryExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> right, std::shared_ptr<const Token> &op)
+ : right(std::move(right)), op(op), Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitUnaryExpr(this);
+ }
+};
+
+struct LiteralExpr : public Expr {
+ std::shared_ptr<const Token> token;
+
+ LiteralExpr(int line, int column, const std::string &file,
+ std::shared_ptr<const Token> &literal)
+ : token(literal), Expr(line, column, file) {}
+ LiteralExpr(int line, int column, const std::string &file,
+ std::shared_ptr<const Token> &&literal)
+ : token(literal), Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitLiteralExpr(this);
+ }
+};
+
+struct SuperExpr : public Expr {
+ std::shared_ptr<const Token> field;
+
+ SuperExpr(int line, int column, const std::string &file,
+ std::shared_ptr<const Token> &field)
+ : field(field), Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitSuperExpr(this);
+ }
+};
+
+struct ThisExpr : public Expr {
+ ThisExpr(int line, int column, const std::string &file)
+ : Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitThisExpr(this);
+ }
+};
+
+struct GroupingExpr : public Expr {
+ std::unique_ptr<Expr> expr;
+
+ GroupingExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &expr)
+ : expr(std::move(expr)), Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitGroupExpr(this);
+ }
+};
+
+struct VariableExpr : public Expr {
+ std::shared_ptr<const Token> variable;
+
+ VariableExpr(int line, int column, const std::string &file,
+ std::shared_ptr<const Token> &variable)
+ : variable(variable), Expr(line, column, file) {}
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitVarExpr(this);
+ }
+};
+
+struct AssignExpr : public Expr {
+ std::shared_ptr<const Token> variable;
+ std::unique_ptr<Expr> value;
+
+ AssignExpr(int line, int column, const std::string &file,
+ std::shared_ptr<const Token> &variable,
+ std::unique_ptr<Expr> &value)
+ : variable(variable), value(std::move(value)), Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitAssignExpr(this);
+ }
+};
+
+struct FnCallExpr : public Expr {
+ std::unique_ptr<Expr> callee;
+ std::vector<std::unique_ptr<Expr>> args;
+
+ FnCallExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &callee,
+ std::vector<std::unique_ptr<Expr>> &args)
+ : callee(std::move(callee)), args(std::move(args)),
+ Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitCallExpr(this);
+ }
+};
+
+struct TernaryExpr : public Expr {
+ std::unique_ptr<Expr> condition;
+ std::unique_ptr<Expr> trueBranch;
+ std::unique_ptr<Expr> falseBranch;
+
+ TernaryExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &condition,
+ std::unique_ptr<Expr> &trueBranch,
+ std::unique_ptr<Expr> &&falseBranch)
+ : condition(std::move(condition)), trueBranch(std::move(trueBranch)),
+ falseBranch(std::move(falseBranch)), Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitTernaryExpr(this);
+ }
+};
+
+struct GetFieldExpr : public Expr {
+ std::shared_ptr<const Token> field;
+ std::unique_ptr<Expr> instance;
+
+ GetFieldExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &instance,
+ std::shared_ptr<const Token> &field)
+ : instance(std::move(instance)), field(field), Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitGetFieldExpr(this);
+ }
+};
+
+struct SetFieldExpr : public Expr {
+ std::shared_ptr<const Token> field;
+ std::unique_ptr<Expr> value;
+ std::unique_ptr<Expr> instance;
+
+ SetFieldExpr(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &instance,
+ std::shared_ptr<const Token> &field,
+ std::unique_ptr<Expr> &value)
+ : instance(std::move(instance)), field(field), value(std::move(value)),
+ Expr(line, column, file) {}
+
+ LBPLType accept(Expression::Visitor *visitor) {
+ return visitor->visitSetFieldExpr(this);
+ }
+};
+
+#endif
--- /dev/null
+#ifndef INTERPRETER_H
+#define INTERPRETER_H
+
+#include "LBPLTypes.h"
+#include "builtin_methods.h"
+#include "environment.h"
+#include "runtime_error.h"
+#include "visitor.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+class BreakException {};
+class ContinueException {};
+class ReturnException {
+public:
+ LBPLType value;
+
+public:
+ ReturnException(LBPLType &&value) : value(value) {}
+};
+
+class Interpreter : Statement::Visitor, Expression::Visitor {
+private:
+ std::shared_ptr<Environment> global, currentEnv;
+ std::map<Expr *, int> locals;
+
+private:
+ void execute(std::unique_ptr<Stmt> &);
+
+ LBPLType evaluate(std::unique_ptr<Expr> &);
+ LBPLType lookupVariable(std::shared_ptr<const Token> &, Expr *);
+ bool isTruthy(const LBPLType &);
+ bool isTruthy(LBPLType &&);
+
+ LBPLType performBinaryOperation(std::shared_ptr<const Token> &,
+ const LBPLType &, const LBPLType &);
+
+ void visitFnStmt(FnStmt *) override;
+ void visitVarStmt(VarStmt *) override;
+ void visitClassStmt(ClassStmt *) override;
+ void visitIfStmt(IfStmt *) override;
+ void visitWhileStmt(WhileStmt *) override;
+ void visitForStmt(ForStmt *) override;
+ void visitScopedStmt(ScopedStmt *) override;
+ void visitExprStmt(ExprStmt *) override;
+ void visitReturnStmt(ReturnStmt *) override;
+
+ LBPLType visitBinaryExpr(BinaryExpr *) override;
+ LBPLType visitBreakExpr(BreakExpr *) override;
+ LBPLType visitContinueExpr(ContinueExpr *) override;
+ LBPLType visitUnaryExpr(UnaryExpr *) override;
+ LBPLType visitLiteralExpr(LiteralExpr *) override;
+ LBPLType visitGroupExpr(GroupingExpr *) override;
+ LBPLType visitSuperExpr(SuperExpr *) override;
+ LBPLType visitThisExpr(ThisExpr *) override;
+ LBPLType visitCallExpr(FnCallExpr *) override;
+ LBPLType visitGetFieldExpr(GetFieldExpr *) override;
+ LBPLType visitSetFieldExpr(SetFieldExpr *) override;
+ LBPLType visitTernaryExpr(TernaryExpr *) override;
+ LBPLType visitVarExpr(VariableExpr *) override;
+ LBPLType visitAssignExpr(AssignExpr *) override;
+
+public:
+ void executeBlock(std::vector<std::unique_ptr<Stmt>> &,
+ std::shared_ptr<Environment> &);
+ void executeBlock(std::vector<std::unique_ptr<Stmt>> &,
+ std::shared_ptr<Environment> &&);
+ void interpret(std::vector<std::unique_ptr<Stmt>> &);
+ void resolve(Expr *, int);
+
+ Interpreter()
+ : global(std::make_shared<Environment>()), currentEnv(global), locals() {
+ global->define("println", std::make_shared<LBPLPrintln>());
+ global->define("clock", std::make_shared<LBPLClock>());
+ }
+};
+
+#endif
--- /dev/null
+#ifndef LEXER_H
+#define LEXER_H
+
+#include "common.h"
+
+#include <fstream>
+#include <iomanip>
+#include <memory>
+#include <vector>
+
+class Lexer {
+private:
+ int line;
+ std::ifstream &stream;
+ std::string filename;
+ std::string currentLine;
+ std::string::iterator start;
+ std::string::iterator current;
+
+public:
+ bool hadError;
+
+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 isAtLineEnd() 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
--- /dev/null
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "common.h"
+#include "interpreter.h"
+#include "parser.h"
+#include "resolver.h"
+#include "statements.h"
+
+#include "ast_printer.h"
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+std::string readFile(const char *);
+
+#endif
--- /dev/null
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "expressions.h"
+#include "lexer.h"
+#include "statements.h"
+#include "syntax_error.h"
+
+#include <fstream>
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#define CONSUME_SEMICOLON(keyword) \
+ consume("Expected ';' after " + std::string(keyword) + " statement.", \
+ 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;
+
+private:
+ bool isAtEnd();
+ void synchronize();
+ std::shared_ptr<const Token> advance();
+
+ template <typename... TokenTypes> bool check(const TokenTypes &...);
+ template <typename... TokenTypes>
+ std::shared_ptr<const Token> consume(const std::string &msg, const TokenTypes &...);
+ template <typename... TokenTypes> bool match(const TokenTypes &...);
+
+ std::vector<std::unique_ptr<Stmt>> importStmt();
+ std::unique_ptr<FnStmt> functionDecl(const std::string &);
+ std::unique_ptr<VarStmt> varDecl();
+ std::unique_ptr<ClassStmt> classDecl();
+
+ std::vector<std::unique_ptr<Stmt>> stmtSequence();
+
+ std::unique_ptr<Stmt> declaration();
+ std::unique_ptr<Stmt> statement();
+ std::unique_ptr<Stmt> ifStmt();
+ std::unique_ptr<Stmt> whileStmt();
+ std::unique_ptr<Stmt> loopStmt();
+ std::unique_ptr<Stmt> forStmt();
+ std::unique_ptr<Stmt> returnStmt();
+ std::unique_ptr<Stmt> scopedStmt();
+ std::unique_ptr<Stmt> expressionStmt();
+
+ std::unique_ptr<Expr> expression();
+ std::unique_ptr<Expr> assignment();
+ std::unique_ptr<Expr> orExpr();
+ std::unique_ptr<Expr> andExpr();
+ std::unique_ptr<Expr> equality();
+ std::unique_ptr<Expr> comparison();
+ std::unique_ptr<Expr> term();
+ std::unique_ptr<Expr> factor();
+ std::unique_ptr<Expr> unary();
+ std::unique_ptr<Expr> call();
+ std::unique_ptr<Expr> primary();
+
+public:
+ Parser(std::ifstream &file, std::string &filename)
+ : lexer(new Lexer(file, filename)), current(lexer->getNextToken()),
+ previous(current) {
+ importedFiles.insert(filename);
+ }
+
+ Parser(std::ifstream &file, const std::string &filename,
+ std::unordered_set<std::string> &importedFiles)
+ : importedFiles(importedFiles), lexer(new Lexer(file, filename)),
+ current(lexer->getNextToken()), previous(current) {}
+
+ std::vector<std::unique_ptr<Stmt>> parse();
+};
+
+#endif
--- /dev/null
+#ifndef RESOLVER_H
+#define RESOLVER_H
+
+#include "environment.h"
+#include "interpreter.h"
+#include "statements.h"
+#include "syntax_error.h"
+#include "visitor.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FunctionType {
+enum Type {
+ None,
+ Function,
+ Initializer,
+};
+}
+
+namespace ClassType {
+enum Type {
+ None,
+ Subclass,
+};
+}
+
+enum VarState {
+ None,
+ Init,
+ Ready,
+};
+
+class Resolver : Statement::Visitor, Expression::Visitor {
+private:
+ Interpreter *interpreter;
+ FunctionType::Type currentFn;
+ ClassType::Type currentClass;
+ int loops;
+ std::vector<std::map<std::string, VarState>> scopes;
+
+public:
+ bool hadError;
+
+private:
+ void beginScope() { scopes.emplace_back(std::map<std::string, VarState>()); }
+ void endScope() { scopes.pop_back(); }
+
+ void declare(const Token *);
+ void define(const Token *);
+
+ void resolveLocal(Expr *, const std::string &);
+ void resolveLocal(Expr *, const Token *);
+ void resolveFunction(FnStmt *, FunctionType::Type);
+
+ void visitFnStmt(FnStmt *) override;
+ void visitVarStmt(VarStmt *) override;
+ void visitClassStmt(ClassStmt *) override;
+ void visitIfStmt(IfStmt *) override;
+ void visitWhileStmt(WhileStmt *) override;
+ void visitForStmt(ForStmt *) override;
+ void visitScopedStmt(ScopedStmt *) override;
+ void visitExprStmt(ExprStmt *) override;
+ void visitReturnStmt(ReturnStmt *) override;
+
+ LBPLType visitBinaryExpr(BinaryExpr *) override;
+ LBPLType visitBreakExpr(BreakExpr *) override;
+ LBPLType visitContinueExpr(ContinueExpr *) override;
+ LBPLType visitUnaryExpr(UnaryExpr *) override;
+ LBPLType visitLiteralExpr(LiteralExpr *) override;
+ LBPLType visitGroupExpr(GroupingExpr *) override;
+ LBPLType visitSuperExpr(SuperExpr *) override;
+ LBPLType visitThisExpr(ThisExpr *) override;
+ LBPLType visitCallExpr(FnCallExpr *) override;
+ LBPLType visitGetFieldExpr(GetFieldExpr *) override;
+ LBPLType visitSetFieldExpr(SetFieldExpr *) override;
+ LBPLType visitTernaryExpr(TernaryExpr *) override;
+ LBPLType visitVarExpr(VariableExpr *) override;
+ LBPLType visitAssignExpr(AssignExpr *) override;
+
+public:
+ Resolver(Interpreter *interpreter)
+ : interpreter(interpreter), currentFn(FunctionType::None),
+ currentClass(ClassType::None), loops(0), scopes() {}
+
+ void resolve(std::vector<std::unique_ptr<Stmt>> &);
+};
+
+#endif
--- /dev/null
+#ifndef RUNTIME_ERROR_H
+#define RUNTIME_ERROR_H
+
+#include "token.h"
+#include "expressions.h"
+#include "statements.h"
+
+#include <string>
+#include <optional>
+#include <iomanip>
+#include <sstream>
+
+struct RuntimeError {
+public:
+ int line;
+ int column;
+ std::string filename;
+ std::string msg;
+
+public:
+ RuntimeError(const Token *errToken, const std::string &msg)
+ : line(errToken->line), column(errToken->column), filename(errToken->filename), msg(msg) {}
+
+ RuntimeError(Expr *errToken, const std::string &msg)
+ : line(errToken->line), column(errToken->column), filename(errToken->file), msg(msg) {}
+
+ RuntimeError(Stmt *errToken, const std::string &msg)
+ : line(errToken->line), column(errToken->column), filename(errToken->filename), msg(msg) {}
+
+ std::string what();
+};
+
+#endif
--- /dev/null
+#ifndef STATEMENTS_H
+#define STATEMENTS_H
+
+#include "expressions.h"
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+struct Stmt {
+ std::string filename;
+ int line, column;
+
+ Stmt(int line, int column, const std::string &filename)
+ : line(line), column(column), filename(filename) {}
+
+ virtual ~Stmt() {}
+ virtual void accept(Statement::Visitor *) = 0;
+};
+
+struct FnStmt : public Stmt {
+ std::shared_ptr<const Token> name;
+ std::vector<std::shared_ptr<const Token> > args;
+ std::vector<std::unique_ptr<Stmt>> body;
+
+ FnStmt(int line, int column, const std::string &file, std::shared_ptr<const Token> &name,
+ std::vector<std::shared_ptr<const Token>> &args, std::vector<std::unique_ptr<Stmt>> &&body)
+ : name(name), args(args), body(std::move(body)), Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitFnStmt(this); }
+};
+
+struct VarStmt : public Stmt {
+ std::shared_ptr<const Token> name;
+ std::unique_ptr<Expr> value;
+
+ VarStmt(int line, int column, const std::string &file, std::shared_ptr<const Token> &name,
+ std::unique_ptr<Expr> &value)
+ : name(name), value(std::move(value)),
+ Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitVarStmt(this); }
+};
+
+struct ClassStmt : public Stmt {
+ std::shared_ptr<const Token> name;
+ std::unique_ptr<VariableExpr> superclass;
+ std::vector<std::unique_ptr<Stmt>> body;
+
+ ClassStmt(int line, int column, const std::string &file, std::shared_ptr<const Token> &name,
+ std::unique_ptr<VariableExpr> &superclass,
+ std::vector<std::unique_ptr<Stmt>> &&body)
+ : name(name), superclass(std::move(superclass)), body(std::move(body)),
+ Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitClassStmt(this); }
+};
+
+struct IfStmt : public Stmt {
+ std::unique_ptr<Expr> condition;
+ std::unique_ptr<Stmt> trueBranch;
+ std::unique_ptr<Stmt> falseBranch;
+
+ IfStmt(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &condition,
+ std::unique_ptr<Stmt> &trueBranch, std::unique_ptr<Stmt> &falseBranch)
+ : condition(std::move(condition)), trueBranch(std::move(trueBranch)),
+ falseBranch(std::move(falseBranch)), Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitIfStmt(this); }
+};
+
+struct WhileStmt : public Stmt {
+ std::unique_ptr<Expr> condition;
+ std::unique_ptr<Stmt> body;
+
+ WhileStmt(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &cond, std::unique_ptr<Stmt> &body)
+ : condition(std::move(cond)), body(std::move(body)),
+ Stmt(line, column, file) {}
+
+ WhileStmt(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &cond, std::unique_ptr<Stmt> &&body)
+ : condition(std::move(cond)), body(std::move(body)),
+ Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitWhileStmt(this); }
+};
+
+struct ForStmt : public Stmt {
+ std::unique_ptr<Expr> increment, condition;
+ std::unique_ptr<Stmt> initializer, body;
+
+ ForStmt(int line, int column, const std::string &file,
+ std::unique_ptr<Stmt> &initializer, std::unique_ptr<Expr> &cond,
+ std::unique_ptr<Expr> &increment, std::unique_ptr<Stmt> &body)
+ : initializer(std::move(initializer)), condition(std::move(cond)),
+ increment(std::move(increment)), body(std::move(body)),
+ Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitForStmt(this); }
+};
+
+struct ScopedStmt : public Stmt {
+ std::vector<std::unique_ptr<Stmt>> body;
+
+ ScopedStmt(int line, int column, const std::string &file,
+ std::vector<std::unique_ptr<Stmt>> &&body)
+ : body(std::move(body)), Stmt(line, column, file) {}
+ void accept(Statement::Visitor *visitor) { visitor->visitScopedStmt(this); }
+};
+
+struct ExprStmt : public Stmt {
+ std::unique_ptr<Expr> expr;
+
+ ExprStmt(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &&expr)
+ : expr(std::move(expr)), Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitExprStmt(this); }
+};
+
+struct ReturnStmt : public Stmt {
+ std::unique_ptr<Expr> value;
+
+ ReturnStmt(int line, int column, const std::string &file,
+ std::unique_ptr<Expr> &value)
+ : value(std::move(value)), Stmt(line, column, file) {}
+
+ void accept(Statement::Visitor *visitor) { visitor->visitReturnStmt(this); }
+};
+
+#endif
--- /dev/null
+#ifndef SYNTAX_ERROR_H
+#define SYNTAX_ERROR_H
+
+#include "token.h"
+#include "expressions.h"
+#include "statements.h"
+
+#include <string>
+#include <optional>
+#include <iomanip>
+#include <sstream>
+
+struct SyntaxError {
+public:
+ int line;
+ int column;
+ std::string filename;
+ std::string msg;
+
+public:
+ SyntaxError(const Token *errToken, const std::string &msg)
+ : line(errToken->line), column(errToken->column), filename(errToken->filename), msg(msg) {}
+
+ SyntaxError(Expr *errExpr, const std::string &msg)
+ : line(errExpr->line), column(errExpr->column), filename(errExpr->file), msg(msg) {}
+
+ SyntaxError(Stmt *errStmt, const std::string &msg)
+ : line(errStmt->line), column(errStmt->column), filename(errStmt->filename), msg(msg) {}
+
+ std::string what();
+};
+
+#endif
--- /dev/null
+#ifndef TOKEN_H
+#define TOKEN_H
+
+#include "token_type.h"
+
+#include <string>
+
+class Token {
+public:
+ int line;
+ int 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) {}
+};
+
+#endif
--- /dev/null
+#ifndef TOKEN_TYPE_H
+#define TOKEN_TYPE_H
+
+enum TokenType {
+ // Single-character tokens.
+ LeftParen,
+ RightParen,
+ LeftBrace,
+ RightBrace,
+ Question,
+ Comma,
+ Dot,
+ Colon,
+ Semicolon,
+ ModOp,
+ Minus,
+ Plus,
+ Slash,
+ Star,
+
+ // One or two character tokens.
+ Bang,
+ BangEqual,
+ Equal,
+ EqualEqual,
+ ShiftRight,
+ Greater,
+ GreaterEqual,
+ ShiftLeft,
+ Less,
+ LessEqual,
+
+ // Literals.
+ Identifier,
+ String,
+ Char,
+ Number,
+
+ // Keywords.
+ Import,
+ Let,
+ And,
+ Class,
+ Else,
+ False,
+ Fn,
+ For,
+ If,
+ Nil,
+ Or,
+ Return,
+ Break,
+ Continue,
+ Super,
+ This,
+ True,
+ While,
+ Loop,
+ Main,
+ Eof,
+ Error,
+};
+
+#endif
--- /dev/null
+#ifndef TREE_NODES_H
+#define TREE_NODES_H
+
+class Stmt;
+class FnStmt;
+class VarStmt;
+class ClassStmt;
+class IfStmt;
+class WhileStmt;
+class ForStmt;
+class ScopedStmt;
+class ExprStmt;
+class ReturnStmt;
+
+class Expr;
+class BinaryExpr;
+class BreakExpr;
+class ContinueExpr;
+class UnaryExpr;
+class LiteralExpr;
+class GroupingExpr;
+class SuperExpr;
+class ThisExpr;
+class FnCallExpr;
+class GetFieldExpr;
+class SetFieldExpr;
+class TernaryExpr;
+class VariableExpr;
+class AssignExpr;
+
+#endif
--- /dev/null
+#ifndef VISITOR_H
+#define VISITOR_H
+
+#include "LBPLTypes.h"
+#include "tree_nodes.h"
+
+#include <iostream>
+
+namespace Statement {
+struct Visitor {
+ virtual void visitFnStmt(FnStmt *) = 0;
+ virtual void visitVarStmt(VarStmt *) = 0;
+ virtual void visitClassStmt(ClassStmt *) = 0;
+ virtual void visitIfStmt(IfStmt *) = 0;
+ virtual void visitWhileStmt(WhileStmt *) = 0;
+ virtual void visitForStmt(ForStmt *) = 0;
+ virtual void visitScopedStmt(ScopedStmt *) = 0;
+ virtual void visitExprStmt(ExprStmt *) = 0;
+ virtual void visitReturnStmt(ReturnStmt *) = 0;
+};
+} // namespace Statement
+
+namespace Expression {
+struct Visitor {
+ virtual LBPLType visitBinaryExpr(BinaryExpr *) = 0;
+ virtual LBPLType visitBreakExpr(BreakExpr *) = 0;
+ virtual LBPLType visitContinueExpr(ContinueExpr *) = 0;
+ virtual LBPLType visitUnaryExpr(UnaryExpr *) = 0;
+ virtual LBPLType visitLiteralExpr(LiteralExpr *) = 0;
+ virtual LBPLType visitGroupExpr(GroupingExpr *) = 0;
+ virtual LBPLType visitSuperExpr(SuperExpr *) = 0;
+ virtual LBPLType visitThisExpr(ThisExpr *) = 0;
+ virtual LBPLType visitCallExpr(FnCallExpr *) = 0;
+ virtual LBPLType visitGetFieldExpr(GetFieldExpr *) = 0;
+ virtual LBPLType visitSetFieldExpr(SetFieldExpr *) = 0;
+ virtual LBPLType visitTernaryExpr(TernaryExpr *) = 0;
+ virtual LBPLType visitVarExpr(VariableExpr *) = 0;
+ virtual LBPLType visitAssignExpr(AssignExpr *) = 0;
+};
+} // namespace Expression
+
+#endif
--- /dev/null
+#include "LBPLClass.h"
+#include "LBPLInstance.h"
+#include "interpreter.h"
+
+LBPLType LBPLClass::call(Interpreter *interpreter, std::vector<LBPLType> &args) {
+ auto instance = std::make_shared<LBPLInstance>(this);
+
+ LBPLFunc *init = findMethod("init");
+ if (init) {
+ init->bind(instance);
+ init->call(interpreter, args);
+ }
+
+ return instance;
+}
+
+LBPLFunc *LBPLClass::findMethod(const std::string &name) {
+ if (methods.contains(name)) {
+ return methods.find(name)->second;
+ } else if (superclass) {
+ return superclass->findMethod(name);
+ }
+
+ return nullptr;
+}
+
+int LBPLClass::arity() {
+ auto it = methods.find("init");
+ return it == methods.end() ? 0 : it->second->arity();
+}
--- /dev/null
+#include "LBPLFunction.h"
+#include "interpreter.h"
+
+void LBPLFunc::bind(std::shared_ptr<LBPLInstance> &&instance) {
+ closureEnv = std::make_shared<Environment>();
+ closureEnv->define("this", instance);
+}
+
+void LBPLFunc::bind(std::shared_ptr<LBPLInstance> &instance) {
+ closureEnv = std::make_shared<Environment>();
+ closureEnv->define("this", instance);
+}
+
+int LBPLFunc::arity() { return stmt->args.size(); }
+
+LBPLType LBPLFunc::call(Interpreter *interpreter, std::vector<LBPLType> &args) {
+ auto env = std::make_shared<Environment>(closureEnv);
+
+ for (int i = 0; i < stmt->args.size(); i++) {
+ env->define(stmt->args[i]->lexeme, args[i]);
+ }
+
+ try {
+ interpreter->executeBlock(stmt->body, env);
+ } catch (ReturnException &ret) {
+ return isInitializer ? closureEnv->getAt(0, "this") : ret.value;
+ }
+
+ return isInitializer ? closureEnv->getAt(0, "this") : nullptr;
+}
--- /dev/null
+#include "LBPLInstance.h"
+
+LBPLType LBPLInstance::get(const Token *name) {
+ if (fields.contains(name->lexeme)) {
+ return fields.find(name->lexeme)->second;
+ }
+
+ LBPLFunc *method = lbplClass->findMethod(name->lexeme);
+ if (method) {
+ method->bind(std::make_shared<LBPLInstance>(this));
+ return std::make_shared<LBPLFunc>(*method);
+ }
+
+ throw RuntimeError(name, "Undefined field '" + name->lexeme + "'.");
+}
+
+void LBPLInstance::set(const Token *name, LBPLType &value) {
+ fields.insert_or_assign(name->lexeme, value);
+}
--- /dev/null
+#include "ast_printer.h"
+#include "statements.h"
+
+#include <iostream>
+
+void AST_Printer::printLocation(Stmt *stmt) {
+ std::cout << "Location: " << stmt->filename << " [Line: " << stmt->line
+ << ", Column: " << stmt->column << "]\n";
+}
+
+void AST_Printer::visitFnStmt(FnStmt *stmt) {
+ printLocation(stmt);
+ std::cout << "(";
+ std::cout << "fn `" + stmt->name->lexeme + "` args[";
+
+ for (auto &&arg : stmt->args) {
+ std::cout << "(" << arg->lexeme << ")";
+ }
+
+ std::cout << "] ` body {\n";
+ for (auto &&bodyStmt : stmt->body) {
+ bodyStmt->accept(this);
+ }
+
+ std::cout << "})\n";
+}
+
+void AST_Printer::visitVarStmt(VarStmt *var) {
+ printLocation(var);
+ std::cout << "(var " << var->name->lexeme << " value `";
+
+ if (var->value) {
+ var->value->accept(this);
+ } else {
+ std::cout << "nil";
+ }
+ std::cout << "`)\n";
+}
+
+void AST_Printer::visitClassStmt(ClassStmt *stmt) {
+ printLocation(stmt);
+ std::cout << "(class `" << stmt->name->lexeme << "` implements(";
+ if (stmt->superclass) {
+ stmt->superclass->accept(this);
+ }
+ std::cout << ") body {\n";
+ for (auto &&fn : stmt->body) {
+ fn->accept(this);
+ }
+ std::cout << "})\n";
+}
+
+void AST_Printer::visitIfStmt(IfStmt *fn) {
+ printLocation(fn);
+ std::cout << "(condition `";
+ fn->condition->accept(this);
+ std::cout << "` if true ";
+ fn->trueBranch->accept(this);
+ std::cout << " if false ";
+ if (fn->falseBranch) {
+ fn->falseBranch->accept(this);
+ }
+ std::cout << ")\n";
+}
+
+void AST_Printer::visitWhileStmt(WhileStmt *stmt) {
+ printLocation(stmt);
+ std::cout << "(while ";
+ stmt->condition->accept(this);
+ std::cout << " do ";
+ stmt->body->accept(this);
+ std::cout << ")\n";
+}
+
+void AST_Printer::visitForStmt(ForStmt *stmt) {
+ printLocation(stmt);
+ std::cout << "(for ";
+ if (stmt->initializer) {
+ stmt->initializer->accept(this);
+ }
+ std::cout << " while ";
+ if (stmt->condition) {
+ stmt->condition->accept(this);
+ } else {
+ std::cout << "yes";
+ }
+ std::cout << " do ";
+ stmt->body->accept(this);
+ std::cout << " then ";
+ if (stmt->increment) {
+ stmt->increment->accept(this);
+ }
+ std::cout << ")\n";
+}
+
+void AST_Printer::visitScopedStmt(ScopedStmt *stmt) {
+ printLocation(stmt);
+ std::cout << "{";
+ for (auto &&stmt : stmt->body) {
+ stmt->accept(this);
+ }
+ std::cout << "}";
+}
+
+void AST_Printer::visitExprStmt(ExprStmt *stmt) {
+ printLocation(stmt);
+ stmt->expr->accept(this);
+ std::cout << "\n";
+}
+
+void AST_Printer::visitReturnStmt(ReturnStmt *stmt) {
+ printLocation(stmt);
+ std::cout << "(return ";
+ if (stmt->value) {
+ stmt->value->accept(this);
+ } else {
+ std::cout << "void";
+ }
+ std::cout << ")\n";
+}
+
+LBPLType AST_Printer::visitBinaryExpr(BinaryExpr *expr) {
+ std::cout << "(" << expr->op->lexeme << " ";
+ expr->left->accept(this);
+ std::cout << " ";
+ expr->right->accept(this);
+ std::cout << ")";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitBreakExpr(BreakExpr *expr) {
+ std::cout << "(breaking)";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitContinueExpr(ContinueExpr *expr) {
+ std::cout << "(continuing)\n";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitUnaryExpr(UnaryExpr *expr) {
+ std::cout << "(" << expr->op->lexeme << " ";
+ expr->right->accept(this);
+ std::cout << ")";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitLiteralExpr(LiteralExpr *expr) {
+ std::cout << expr->token->lexeme;
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitSuperExpr(SuperExpr *expr) {
+ std::cout << "(super)";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitThisExpr(ThisExpr *expr) {
+ std::cout << "(this)";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitAssignExpr(AssignExpr *expr) {
+ std::cout << "(assign ";
+ expr->value->accept(this);
+ std::cout << " to " << expr->variable->lexeme << ")";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitGroupExpr(GroupingExpr *expr) {
+ expr->expr->accept(this);
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitCallExpr(FnCallExpr *expr) {
+ std::cout << "(calling `";
+ expr->callee->accept(this);
+ std::cout << "` with parameters [";
+
+ for (auto &&arg : expr->args) {
+ arg->accept(this);
+ }
+
+ std::cout << "])";
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitGetFieldExpr(GetFieldExpr *expr) {
+ std::cout << "get field `" << expr->field->lexeme << "` from ";
+ expr->instance->accept(this);
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitSetFieldExpr(SetFieldExpr *expr) {
+ std::cout << "setting field `" << expr->field->lexeme << "` from ";
+ expr->instance->accept(this);
+ std::cout << " to ";
+ expr->value->accept(this);
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitTernaryExpr(TernaryExpr *expr) {
+ std::cout << "condition `";
+ expr->condition->accept(this);
+ std::cout << "` if true ";
+ expr->trueBranch->accept(this);
+ std::cout << " if false ";
+ expr->falseBranch->accept(this);
+ return nullptr;
+}
+
+LBPLType AST_Printer::visitVarExpr(VariableExpr *expr) {
+ std::cout << expr->variable->lexeme;
+ return nullptr;
+}
--- /dev/null
+#include "environment.h"
+#include "LBPLTypes.h"
+#include "runtime_error.h"
+#include <memory>
+#include <string>
+#include <utility>
+#include <variant>
+
+void Environment::define(const std::string &name, LBPLType &value) {
+ env.insert(std::make_pair(name, value));
+}
+void Environment::define(const std::string &name, LBPLType &&value) {
+ env.insert(std::make_pair(name, value));
+}
+
+
+LBPLType Environment::get(std::shared_ptr<const Token> &name) {
+ auto it = env.find(name->lexeme);
+
+ if (it != env.end()) {
+ return it->second;
+ }
+ if (enclosing) {
+ return enclosing->get(name);
+ }
+
+ throw RuntimeError(name.get(), "Undefined name '"+name->lexeme+"'.");
+}
+
+LBPLType Environment::getAt(int depth, std::shared_ptr<const Token> &name) {
+ return getAt(depth, name->lexeme);
+}
+
+LBPLType Environment::getAt(int depth, const std::string &name) {
+ if (depth > 0) {
+ return enclosing->getAt(depth-1, name);
+ }
+
+ auto it = env.find(name);
+ return it != env.end() ? it->second : nullptr;
+}
+
+void Environment::assign(std::shared_ptr<const Token> &name, LBPLType &value) {
+ if (env.contains(name->lexeme)) {
+ env.insert_or_assign(name->lexeme, value);
+ } else if (enclosing) {
+ enclosing->assign(name, value);
+ } else {
+ throw RuntimeError(name.get(), "Undefined variable '"+name->lexeme+"'.");
+ }
+}
+
+void Environment::assign(std::shared_ptr<const Token> &name, LBPLType &&value) {
+ assign(name, value);
+}
+
+void Environment::assignAt(int depth, std::shared_ptr<const Token> &name, LBPLType &value) {
+ if (depth > 0) {
+ enclosing->assignAt(depth-1, name, value);
+ }
+
+ env.insert_or_assign(name->lexeme, value);
+}
+
+void Environment::printEnv(const std::string &&msg) {
+ std::cout << "========"<< msg <<"=========" << std::endl;
+ for (const auto& [key, value] : env) {
+ std::cout << "\t" << key << ": ";
+
+ if (std::holds_alternative<int>(value)) {
+ std::cout << "int" << std::endl;
+ } else if (std::holds_alternative<double>(value)) {
+ std::cout << "double" << std::endl;
+ } else if (std::holds_alternative<char>(value)) {
+ std::cout << "char" << std::endl;
+ } else if (std::holds_alternative<std::nullptr_t>(value)) {
+ std::cout << "nullptr" << std::endl;
+ } else if (std::holds_alternative<std::shared_ptr<LBPLCallable>>(value)) {
+ std::cout << "callable" << std::endl;
+ } else if (std::holds_alternative<std::shared_ptr<LBPLClass>>(value)) {
+ std::cout << "class" << std::endl;
+ } else if (std::holds_alternative<std::shared_ptr<LBPLInstance>>(value)) {
+ std::cout << "instance" << std::endl;
+ } else {
+ std::cout << "idk" << std::endl;
+ }
+ }
+ std::cout << "===================================" << std::endl;
+}
--- /dev/null
+#include "interpreter.h"
+#include "LBPLClass.h"
+#include "LBPLFunction.h"
+#include "LBPLInstance.h"
+#include "LBPLTypes.h"
+#include "runtime_error.h"
+
+#include <chrono>
+#include <cstddef>
+#include <map>
+#include <memory>
+#include <string>
+#include <variant>
+class Timer {
+ std::chrono::time_point<std::chrono::high_resolution_clock> m_startTimepoint;
+
+public:
+ Timer() { m_startTimepoint = std::chrono::high_resolution_clock::now(); }
+
+ ~Timer() { stop(); }
+
+ void stop() {
+ auto endTimepoint = std::chrono::high_resolution_clock::now();
+
+ auto start = std::chrono::time_point_cast<std::chrono::microseconds>(
+ m_startTimepoint)
+ .time_since_epoch()
+ .count();
+ auto end =
+ std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint)
+ .time_since_epoch()
+ .count();
+
+ auto duration = end - start;
+ auto ms = duration * 0.001;
+
+ std::cout << duration << "µs (" << ms << "ms)\n";
+ }
+};
+
+void Interpreter::interpret(std::vector<std::unique_ptr<Stmt>> &stmts) {
+ std::cout << "\tinterpreter begin\n";
+ try {
+ for (auto &&stmt : stmts) {
+ stmt->accept(this);
+ }
+ } catch (RuntimeError &e) {
+ std::cout << e.what();
+ }
+}
+
+void Interpreter::resolve(Expr *expr, int depth) {
+ locals.insert_or_assign(expr, depth);
+}
+
+void Interpreter::visitFnStmt(FnStmt *stmt) {
+ currentEnv->define(stmt->name->lexeme,
+ std::make_shared<LBPLFunc>(stmt, currentEnv, false));
+}
+
+void Interpreter::visitVarStmt(VarStmt *stmt) {
+ LBPLType value;
+ if (stmt->value) {
+ value = stmt->value->accept(this);
+ }
+
+ currentEnv->define(stmt->name->lexeme, value);
+}
+
+void Interpreter::visitClassStmt(ClassStmt *stmt) {
+ LBPLType superclass;
+ currentEnv->define(stmt->name->lexeme, nullptr);
+
+ if (stmt->superclass) {
+ superclass = stmt->superclass->accept(this);
+ if (!std::holds_alternative<std::shared_ptr<LBPLClass>>(superclass)) {
+ throw RuntimeError(stmt->superclass.get(),
+ "Superclass must be another class.");
+ }
+
+ currentEnv = std::make_shared<Environment>(currentEnv);
+ currentEnv->define("super",
+ std::get<std::shared_ptr<LBPLClass>>(superclass));
+ }
+
+ std::map<std::string, LBPLFunc *> methods;
+ for (auto &&methodStmt : stmt->body) {
+ auto method = dynamic_cast<FnStmt *>(methodStmt.get());
+ if (!method) {
+ 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));
+ }
+
+ if (stmt->superclass) {
+ currentEnv = currentEnv->enclosing;
+ currentEnv->assign(stmt->name,
+ std::make_shared<LBPLClass>(
+ stmt->name->lexeme,
+ std::get<std::shared_ptr<LBPLClass>>(superclass),
+ methods));
+ } else {
+ currentEnv->assign(
+ stmt->name, std::make_shared<LBPLClass>(stmt->name->lexeme, methods));
+ }
+}
+
+void Interpreter::visitIfStmt(IfStmt *stmt) {
+ if (isTruthy(stmt->condition->accept(this))) {
+ stmt->trueBranch->accept(this);
+ } else if (stmt->falseBranch) {
+ stmt->falseBranch->accept(this);
+ }
+}
+
+void Interpreter::visitWhileStmt(WhileStmt *stmt) {
+ try {
+ while (isTruthy(stmt->condition->accept(this))) {
+ try {
+ stmt->body->accept(this);
+ } catch (ContinueException &e) {
+ }
+ }
+ } catch (BreakException &e) {
+ }
+}
+
+void Interpreter::visitForStmt(ForStmt *stmt) {
+ auto env = currentEnv;
+ currentEnv = std::make_shared<Environment>(currentEnv);
+
+ stmt->initializer->accept(this);
+
+ try {
+ while (isTruthy(stmt->condition->accept(this))) {
+ try {
+ stmt->body->accept(this);
+ } catch (ContinueException &e) {
+ }
+
+ stmt->increment->accept(this);
+ }
+ } catch (BreakException &e) {
+ }
+
+ currentEnv = env;
+}
+
+void Interpreter::visitScopedStmt(ScopedStmt *stmt) {
+ executeBlock(stmt->body, std::make_shared<Environment>(currentEnv));
+}
+
+void Interpreter::visitExprStmt(ExprStmt *stmt) { stmt->expr->accept(this); }
+void Interpreter::visitReturnStmt(ReturnStmt *stmt) {
+ throw ReturnException(stmt->value->accept(this));
+}
+
+LBPLType Interpreter::visitBinaryExpr(BinaryExpr *expr) {
+ LBPLType left = expr->left->accept(this);
+ LBPLType right = expr->right->accept(this);
+ return performBinaryOperation(expr->op, left, right);
+
+ /* if (std::holds_alternative<int>(left) &&
+ * std::holds_alternative<int>(right)) { */
+ /* return performBinaryOperation(expr->op.get(), std::get<int>(left), */
+ /* std::get<int>(right)); */
+ /* } else if (std::holds_alternative<double>(left) && */
+ /* std::holds_alternative<double>(right)) { */
+ /* return performBinaryOperation(expr->op.get(), std::get<double>(left), */
+ /* std::get<double>(right)); */
+ /* } else if (std::holds_alternative<double>(left) && */
+ /* std::holds_alternative<int>(right)) { */
+ /* return performBinaryOperation(expr->op.get(), std::get<double>(left), */
+ /* std::get<int>(right)); */
+ /* } else if (std::holds_alternative<int>(left) && */
+ /* std::holds_alternative<double>(right)) { */
+ /* return performBinaryOperation(expr->op.get(), std::get<int>(left), */
+ /* std::get<double>(right)); */
+ /* } else if ((std::holds_alternative<std::string>(left) || */
+ /* std::holds_alternative<std::string>(right)) && */
+ /* expr->op->type == TokenType::Plus) { */
+ /* if (std::holds_alternative<std::string>(left) && */
+ /* std::holds_alternative<std::string>(right)) { */
+ /* return std::get<std::string>(left) + */
+ /* std::get<std::string>(right); */
+ /* } else if (std::holds_alternative<std::string>(left) && */
+ /* std::holds_alternative<int>(right)) { */
+ /* return std::get<std::string>(left) + */
+ /* std::to_string(std::get<int>(right)); */
+ /* } else if (std::holds_alternative<int>(left) && */
+ /* std::holds_alternative<std::string>(right)) { */
+ /* return std::get<std::string>(left) + */
+ /* std::to_string(std::get<double>(right)); */
+ /* } else if (std::holds_alternative<double>(left) && */
+ /* std::holds_alternative<std::string>(right)) { */
+ /* return std::to_string(std::get<double>(left)) + */
+ /* std::get<std::string>(right); */
+ /* } else if (std::holds_alternative<std::string>(left) && */
+ /* std::holds_alternative<char>(right)) { */
+ /* return std::get<std::string>(left) + std::get<char>(right); */
+ /* } else if (std::holds_alternative<char>(left) && */
+ /* std::holds_alternative<std::string>(right)) { */
+ /* return std::get<char>(left) + std::get<std::string>(right); */
+ /* } */
+ /* } */
+
+ /* throw RuntimeError(expr->op.get(), "Invalid binary operation operand."); */
+}
+
+LBPLType Interpreter::visitBreakExpr(BreakExpr *) { throw BreakException(); }
+LBPLType Interpreter::visitContinueExpr(ContinueExpr *) {
+ throw ContinueException();
+}
+
+LBPLType Interpreter::visitUnaryExpr(UnaryExpr *expr) {
+ LBPLType right = expr->right->accept(this);
+
+ if (expr->op->type == TokenType::Minus) {
+ if (std::holds_alternative<int>(right)) {
+ return -std::get<int>(right);
+ } else if (std::holds_alternative<double>(right)) {
+ return -std::get<double>(right);
+ }
+ } else if (expr->op->type == TokenType::Bang) {
+ return !isTruthy(right);
+ }
+
+ return nullptr;
+}
+
+LBPLType Interpreter::visitLiteralExpr(LiteralExpr *expr) {
+ if (expr->token->type == TokenType::True) {
+ return true;
+ } else if (expr->token->type == TokenType::False) {
+ return false;
+ } else if (expr->token->type == TokenType::Nil) {
+ return nullptr;
+ } else if (expr->token->type == TokenType::Char) {
+ return expr->token->lexeme[0];
+ } else if (expr->token->type == TokenType::String) {
+ return expr->token->lexeme;
+ } else if (expr->token->type == TokenType::Number) {
+ if (expr->token->lexeme.find('.') == std::string::npos) {
+ return std::stoi(expr->token->lexeme);
+ }
+ return std::stod(expr->token->lexeme);
+ } else if (expr->token->type == TokenType::Identifier) {
+ return lookupVariable(expr->token, expr);
+ }
+
+ return nullptr;
+}
+
+LBPLType Interpreter::visitGroupExpr(GroupingExpr *expr) {
+ return expr->expr->accept(this);
+}
+
+LBPLType Interpreter::visitSuperExpr(SuperExpr *) { return nullptr; }
+LBPLType Interpreter::visitThisExpr(ThisExpr *) { return nullptr; }
+LBPLType Interpreter::visitCallExpr(FnCallExpr *expr) {
+ LBPLType callee = expr->callee->accept(this);
+
+ std::vector<LBPLType> args;
+ args.reserve(expr->args.size());
+
+ for (auto &&arg : expr->args) {
+ args.emplace_back(arg->accept(this));
+ }
+
+ if (std::holds_alternative<std::shared_ptr<LBPLCallable>>(callee)) {
+ auto function = std::get<std::shared_ptr<LBPLCallable>>(callee);
+ if (function->arity() != args.size()) {
+ throw RuntimeError(expr->callee.get(), "Wrong number of arguments.");
+ }
+
+ return function->call(this, args);
+ } else if (std::holds_alternative<std::shared_ptr<LBPLClass>>(callee)) {
+ auto clas = std::get<std::shared_ptr<LBPLClass>>(callee);
+ if (clas->arity() != args.size()) {
+ throw RuntimeError(expr->callee.get(), "Wrong number of arguments.");
+ }
+
+ return clas->call(this, args);
+ }
+
+ throw RuntimeError(expr->callee.get(),
+ "Can only call a function or class initializer.");
+}
+
+LBPLType Interpreter::visitGetFieldExpr(GetFieldExpr *expr) {
+ LBPLType instance = expr->instance->accept(this);
+ if (std::holds_alternative<std::shared_ptr<LBPLInstance>>(instance)) {
+ return std::get<std::shared_ptr<LBPLInstance>>(instance)->get(expr->field.get());
+ }
+
+ throw RuntimeError(expr->instance.get(), "Only instances of classes can have properties");
+}
+
+LBPLType Interpreter::visitSetFieldExpr(SetFieldExpr *expr) {
+ LBPLType instance = expr->instance->accept(this);
+
+ if (std::holds_alternative<std::shared_ptr<LBPLInstance>>(instance)) {
+ LBPLType value = expr->value->accept(this);
+ std::get<std::shared_ptr<LBPLInstance>>(instance)->set(expr->field.get(), value);
+ } else {
+ throw RuntimeError(expr->instance.get(), "Only instances of classes can have properties");
+ }
+
+ return nullptr;
+}
+
+LBPLType Interpreter::visitTernaryExpr(TernaryExpr *expr) {
+ if (isTruthy(expr->condition->accept(this))) {
+ return expr->trueBranch->accept(this);
+ }
+
+ return expr->falseBranch->accept(this);
+}
+
+LBPLType Interpreter::visitVarExpr(VariableExpr *expr) {
+ return lookupVariable(expr->variable, expr);
+}
+LBPLType Interpreter::visitAssignExpr(AssignExpr *expr) {
+ LBPLType value = expr->value->accept(this);
+
+ auto it = locals.find(expr);
+ if (it == locals.end()) {
+ global->assign(expr->variable, value);
+ } else {
+ currentEnv->assignAt(it->second, expr->variable, value);
+ }
+
+ return value;
+}
+
+void Interpreter::execute(std::unique_ptr<Stmt> &stmt) { stmt->accept(this); }
+
+LBPLType Interpreter::evaluate(std::unique_ptr<Expr> &expr) {
+ return expr->accept(this);
+}
+
+LBPLType Interpreter::performBinaryOperation(std::shared_ptr<const Token> &op,
+ const LBPLType &left,
+ const LBPLType &right) {
+ auto performIntOp = [](int l, int r,
+ std::shared_ptr<const Token> &op) -> LBPLType {
+ switch (op->type) {
+ case TokenType::Plus:
+ return l + r;
+ case TokenType::Minus:
+ return l - r;
+ case TokenType::Star:
+ return l * r;
+ case TokenType::Slash:
+ if (r == 0) {
+ throw RuntimeError(op.get(), "Division by zero.");
+ }
+ return l / r;
+ case TokenType::Less:
+ return l < r;
+ case TokenType::LessEqual:
+ return l <= r;
+ case TokenType::Greater:
+ return l > r;
+ case TokenType::GreaterEqual:
+ return l >= r;
+ case TokenType::EqualEqual:
+ return l == r;
+ case TokenType::BangEqual:
+ return l != r;
+ case TokenType::ModOp:
+ if (r == 0) {
+ throw RuntimeError(op.get(), "Modulo by zero.");
+ }
+ return l % r;
+ default:
+ throw RuntimeError(op.get(), "Unsupported binary operation.");
+ }
+ };
+
+ auto performDoubleOp = [](double l, double r,
+ std::shared_ptr<const Token> &op) -> LBPLType {
+ switch (op->type) {
+ case TokenType::Plus:
+ return l + r;
+ case TokenType::Minus:
+ return l - r;
+ case TokenType::Star:
+ return l * r;
+ case TokenType::Slash:
+ if (r == 0) {
+ throw RuntimeError(op.get(), "Division by zero.");
+ }
+ return l / r;
+ case TokenType::Less:
+ return l < r;
+ case TokenType::LessEqual:
+ return l <= r;
+ case TokenType::Greater:
+ return l > r;
+ case TokenType::GreaterEqual:
+ return l >= r;
+ case TokenType::EqualEqual:
+ return l == r;
+ case TokenType::BangEqual:
+ return l != r;
+ default:
+ throw RuntimeError(op.get(), "Unsupported binary operation.");
+ }
+ };
+
+ auto performStringOp = [](const std::string &l, const std::string &r,
+ std::shared_ptr<const Token> &op) -> LBPLType {
+ if (op->type == TokenType::Plus) {
+ return l + r;
+ } else {
+ throw RuntimeError(op.get(), "Unsupported binary operation.");
+ }
+ };
+
+ return std::visit(
+ [&](const auto &l, const auto &r) -> LBPLType {
+ using L = std::decay_t<decltype(l)>;
+ using R = std::decay_t<decltype(r)>;
+
+ if constexpr (std::is_same_v<L, int> && std::is_same_v<R, int>) {
+ return performIntOp(l, r, op);
+ } else if constexpr (std::is_same_v<L, double> &&
+ std::is_same_v<R, double>) {
+ return performDoubleOp(l, r, op);
+ } else if constexpr ((std::is_same_v<L, std::string> &&
+ std::is_same_v<R, int>) ||
+ (std::is_same_v<L, int> &&
+ std::is_same_v<R, std::string>)) {
+ // Handle int-to-string or string-to-int concatenation
+ std::string leftStr = std::holds_alternative<int>(left)
+ ? std::to_string(std::get<int>(left))
+ : std::get<std::string>(left);
+ std::string rightStr = std::holds_alternative<int>(right)
+ ? std::to_string(std::get<int>(right))
+ : std::get<std::string>(right);
+ if (op->type == TokenType::Plus) {
+ return leftStr + rightStr;
+ } else {
+ throw RuntimeError(op.get(), "Unsupported binary operation.");
+ }
+ } else if constexpr ((std::is_same_v<L, std::string> &&
+ std::is_same_v<R, double>) ||
+ (std::is_same_v<L, double> &&
+ std::is_same_v<R, std::string>)) {
+ // Handle double-to-string or string-to-double concatenation
+ std::string leftStr = std::holds_alternative<double>(left)
+ ? std::to_string(std::get<double>(left))
+ : std::get<std::string>(left);
+ std::string rightStr = std::holds_alternative<double>(right)
+ ? std::to_string(std::get<double>(right))
+ : std::get<std::string>(right);
+ if (op->type == TokenType::Plus) {
+ return leftStr + rightStr;
+ } else {
+ throw RuntimeError(op.get(), "Unsupported binary operation.");
+ }
+ } else if constexpr (std::is_same_v<L, std::string> &&
+ std::is_same_v<R, std::string>) {
+ return performStringOp(l, r, op);
+ } else {
+ throw RuntimeError(op.get(), "Unsupported binary operation.");
+ }
+ },
+ left, right);
+}
+
+/* template <typename T, typename G> */
+/* LBPLType Interpreter::performBinaryOperation(const Token *op, const T &left,
+ */
+/* const G &right) { */
+/* switch (op->type) { */
+/* case TokenType::EqualEqual: */
+/* return left == right; */
+/* case TokenType::BangEqual: */
+/* return left != right; */
+/* case TokenType::Less: */
+/* return left < right; */
+/* case TokenType::LessEqual: */
+/* return left <= right; */
+/* case TokenType::Greater: */
+/* return left > right; */
+/* case TokenType::GreaterEqual: */
+/* return left >= right; */
+/* case TokenType::Plus: */
+/* return left + right; */
+/* case TokenType::Minus: */
+/* return left - right; */
+/* case TokenType::Star: */
+/* return left * right; */
+/* case TokenType::Slash: */
+/* if (right == T(0)) { */
+/* throw RuntimeError(op, "Division by zero."); */
+/* } */
+/* return left / right; */
+/* case TokenType::ModOp: */
+/* return (int)left % (int)right; */
+/* default: */
+/* throw RuntimeError(op, "Unsupported binary operation."); */
+/* } */
+/* } */
+
+void Interpreter::executeBlock(std::vector<std::unique_ptr<Stmt>> &body,
+ std::shared_ptr<Environment> &&env) {
+ executeBlock(body, env);
+}
+
+void Interpreter::executeBlock(std::vector<std::unique_ptr<Stmt>> &body,
+ std::shared_ptr<Environment> &env) {
+ auto prev = currentEnv;
+
+ try {
+ currentEnv = env;
+ for (auto &&stmt : body) {
+ stmt->accept(this);
+ }
+ } catch (ReturnException &e) {
+ currentEnv = prev;
+ throw e;
+ }
+
+ currentEnv = prev;
+}
+
+bool Interpreter::isTruthy(const LBPLType &value) {
+ if (std::holds_alternative<std::nullptr_t>(value)) {
+ return std::get<std::nullptr_t>(value) != nullptr;
+ } else if (std::holds_alternative<bool>(value)) {
+ return std::get<bool>(value);
+ } else if (std::holds_alternative<int>(value)) {
+ return std::get<int>(value) == 0;
+ } else if (std::holds_alternative<double>(value)) {
+ return std::get<double>(value) == 0;
+ }
+
+ return false;
+}
+
+bool Interpreter::isTruthy(LBPLType &&value) { return isTruthy(value); }
+
+LBPLType Interpreter::lookupVariable(std::shared_ptr<const Token> &name,
+ Expr *expr) {
+ auto it = locals.find(expr);
+
+ if (it == locals.end()) {
+ return global->get(name);
+ }
+
+ return currentEnv->getAt(it->second, name);
+}
--- /dev/null
+#include "lexer.h"
+
+Lexer::Lexer(std::ifstream &stream, const std::string &filename)
+ : stream(stream), line(1), hadError(false), filename(filename) {
+ goToNextLine();
+}
+
+bool Lexer::isAtEnd() const { return stream.eof(); }
+bool Lexer::isAtLineEnd() 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 == '_';
+}
+bool Lexer::match(char ch) {
+ if (isAtLineEnd() || ch != *current) {
+ return false;
+ }
+
+ advance();
+ return true;
+}
+
+void Lexer::goToNextLine() {
+ std::getline(stream, currentLine);
+ current = currentLine.begin();
+ start = currentLine.begin();
+}
+
+char Lexer::peek() const { return *current; }
+char Lexer::peekNext() const { return *(current + 1); }
+char Lexer::advance() {
+ if (isAtLineEnd()) {
+ if (isAtEnd()) {
+ return '\0';
+ }
+
+ std::getline(stream, currentLine);
+ current = currentLine.begin();
+
+ return *current;
+ }
+
+ return *(current++);
+}
+
+TokenType Lexer::checkKeyword(int startIndex, const std::string &restOfKeyword,
+ TokenType typeIfMatch) {
+ if (current - (start + startIndex) != restOfKeyword.length()) {
+ return TokenType::Identifier;
+ }
+
+ for (int i = 0; i < restOfKeyword.length(); i++) {
+ if (*(start + startIndex + i) != restOfKeyword[i]) {
+ return TokenType::Identifier;
+ }
+ }
+
+ return typeIfMatch;
+}
+
+TokenType Lexer::isIdentifierOrKeywork() {
+ switch (*start) {
+ case 'b':
+ return checkKeyword(1, "reak", TokenType::Break);
+ case 'c':
+ if (current - start > 1) {
+ switch (*(start + 1)) {
+ case 'l':
+ return checkKeyword(2, "ass", TokenType::Class);
+ case 'o':
+ return checkKeyword(2, "ntinue", TokenType::Continue);
+ }
+ }
+
+ break;
+ case 'e':
+ return checkKeyword(1, "lse", TokenType::Else);
+ case 'f':
+ if (current - start > 1) {
+ switch (*(start + 1)) {
+ case 'a':
+ return checkKeyword(2, "lse", TokenType::False);
+ case 'o':
+ return checkKeyword(2, "r", TokenType::For);
+ case 'n':
+ return checkKeyword(2, "", TokenType::Fn);
+ }
+ }
+
+ break;
+ case 'i':
+ if (current - start > 1) {
+ switch (*(start + 1)) {
+ case 'f':
+ return checkKeyword(2, "", TokenType::If);
+ case 'm':
+ return checkKeyword(2, "port", TokenType::Import);
+ }
+ }
+
+ break;
+ case 'l':
+ if (current - start > 1) {
+ switch (*(start + 1)) {
+ case 'e':
+ return checkKeyword(2, "t", TokenType::Let);
+ case 'o':
+ return checkKeyword(2, "op", TokenType::Loop);
+ }
+ }
+ break;
+ case 'n':
+ return checkKeyword(1, "il", TokenType::Nil);
+ case 'r':
+ return checkKeyword(1, "eturn", TokenType::Return);
+ case 's':
+ return checkKeyword(1, "uper", TokenType::Super);
+ case 't':
+ if (current - start > 1) {
+ switch (*(start + 1)) {
+ case 'r':
+ return checkKeyword(2, "ue", TokenType::True);
+ case 'h':
+ return checkKeyword(2, "is", TokenType::This);
+ }
+ }
+ break;
+ case 'w':
+ return checkKeyword(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,
+ start - currentLine.begin(), filename);
+ }
+
+ return std::make_shared<const Token>(type, std::string(start, current), line,
+ start - currentLine.begin(), 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,
+ start - currentLine.begin(), filename);
+}
+
+void Lexer::skipWhitespace() {
+ do {
+ switch (peek()) {
+ case '\0':
+ line++;
+ case ' ':
+ case '\r':
+ case '\t':
+ advance();
+ break;
+ case '#':
+ while (!isAtLineEnd()) {
+ 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 operator '|' in: `" + currentLine +
+ "`.\n\tDid you mean '|\033[4;32m|\033[0m'?");
+ 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.");
+ goToNextLine();
+ return res;
+ }
+ return makeToken(TokenType::Char, val);
+ }
+ case '"': {
+ std::string lexeme;
+
+ while (peek() != '"' && !isAtLineEnd()) {
+ 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 (isAtLineEnd()) {
+ return makeErrorToken("Unterminated string.");
+ }
+
+ advance();
+ return makeToken(TokenType::String, lexeme);
+ }
+ }
+
+ if (isAtEnd()) {
+ return makeToken(TokenType::Eof, "EoF");
+ }
+
+ return makeErrorToken("\033[1;36mHow did you get here?\033[0m");
+}
+
+int Lexer::getLine() { return line; }
+int Lexer::getColumn() { return current - currentLine.begin(); }
+std::string Lexer::getFilename() { return filename; }
--- /dev/null
+#include "main.h"
+
+int main(const int argc, const char **argv) {
+ if (argc < 2) {
+ std::cerr << "\033[1;31mNot enough arguemnts.\tUsage: lbpl [script]" << std::endl;
+ return 1;
+ }
+
+ std::ifstream sourceFile(argv[1]);
+ std::string filename(argv[1]);
+ if (!sourceFile.is_open()) {
+ std::cerr << "Error opening the file." << std::endl;
+ return 1;
+ }
+
+ Parser *parser = new Parser(sourceFile, filename);
+ std::vector<std::unique_ptr<Stmt>> statements = parser->parse();
+
+ // AST_Printer test;
+ // for (auto &&stmt : statements) {
+ // stmt->accept(&test);
+ // }
+
+ if (!parser->hadError) {
+ Interpreter *interpreter = new Interpreter();
+ Resolver *resolver = new Resolver(interpreter);
+ resolver->resolve(statements);
+
+ if (!resolver->hadError) {
+ interpreter->interpret(statements);
+ }
+
+ delete resolver;
+ delete interpreter;
+ }
+
+ delete parser;
+ sourceFile.close();
+}
--- /dev/null
+#include "parser.h"
+
+// static size_t totalMem = 0;
+// void *operator new(size_t size) {
+// totalMem += size;
+// void *ptr = malloc(size + sizeof(size_t));
+// if (ptr) {
+// *((size_t *)ptr) = size;
+// return (char *)ptr + sizeof(size_t);
+// } else {
+// throw std::bad_alloc();
+// }
+// // return malloc(size);
+// }
+
+// void operator delete(void *ptr) noexcept {
+// if (ptr) {
+// size_t *originalPtr = (size_t *)((char *)ptr - sizeof(size_t));
+// size_t size = *originalPtr;
+// totalMem -= size;
+// free(originalPtr);
+// }
+// }
+
+std::vector<std::unique_ptr<Stmt>> Parser::parse() {
+ std::vector<std::unique_ptr<Stmt>> stmts;
+
+ // {
+ // Timer timer;
+ while (!isAtEnd()) {
+ try {
+ if (match(TokenType::Import)) {
+ for (auto &&stmt : importStmt()) {
+ stmts.emplace_back(std::move(stmt));
+ }
+ } else {
+ stmts.emplace_back(declaration());
+ }
+ } catch (SyntaxError &e) {
+ hadError = true;
+ std::cout << e.what();
+ synchronize();
+ }
+ }
+ // }
+
+ // std::cout << totalMem << "B\n";
+ return stmts;
+}
+
+std::unique_ptr<Stmt> Parser::declaration() {
+ if (match(TokenType::Let)) {
+ return varDecl();
+ } else if (match(TokenType::Fn)) {
+ return functionDecl("function");
+ } else if (match(TokenType::Class)) {
+ return classDecl();
+ }
+
+ return statement();
+}
+
+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 + "'.",
+ TokenType::String);
+
+ if (importedFiles.contains(path->lexeme)) {
+ throw SyntaxError(previous.get(),
+ "Recursive file import: '" + path->lexeme +
+ "' has already been imported or is the main file.");
+ }
+ importedFiles.insert(path->lexeme);
+
+ std::ifstream sourceFile(path->lexeme);
+ if (!sourceFile.is_open()) {
+ throw SyntaxError(previous.get(), "Error opening file: '" + path->lexeme +
+ "'.\nIs the path correct?");
+ }
+
+ CONSUME_SEMICOLON("import");
+
+ return (new Parser(sourceFile, path->lexeme, importedFiles))->parse();
+}
+
+std::unique_ptr<VarStmt> Parser::varDecl() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ std::shared_ptr<const Token> name =
+ consume("Expected variable name after 'let' keyword but instead got '" +
+ current->lexeme + "'.",
+ TokenType::Identifier);
+
+ std::unique_ptr<Expr> value;
+ if (match(TokenType::Equal)) {
+ value = expression();
+ }
+
+ consume("Expected ';' at the end of a statement but instead got: '" +
+ current->lexeme + "'.",
+ TokenType::Semicolon);
+ return std::make_unique<VarStmt>(line, col, lexer->getFilename(), 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();
+ 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 + "'.",
+ 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 + "'.",
+ TokenType::Identifier);
+ args.push_back(arg);
+ } while (match(TokenType::Comma));
+ }
+ consume("Expected ')' but instead got '" + current->lexeme + "'.",
+ TokenType::RightParen);
+ consume("Expected '{' after function signature but instead got '" +
+ current->lexeme + "'.",
+ TokenType::LeftBrace);
+
+ return std::make_unique<FnStmt>(line, col, lexer->getFilename(), name, args,
+ stmtSequence());
+}
+
+std::unique_ptr<ClassStmt> Parser::classDecl() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ std::shared_ptr<const Token> name =
+ consume("Expected class name but instead got '" + current->lexeme + "'.",
+ 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);
+ }
+
+ auto clas = std::make_unique<ClassStmt>(line, col, lexer->getFilename(), name,
+ superclass,
+ std::vector<std::unique_ptr<Stmt>>());
+ if (match(TokenType::Semicolon)) {
+ return clas;
+ } else if (match(TokenType::LeftBrace)) {
+ while (!check(TokenType::RightBrace) && !isAtEnd()) {
+ clas->body.emplace_back(functionDecl("method"));
+ }
+ consume("Expected closing '}'.", TokenType::RightBrace);
+
+ return clas;
+ } else {
+ throw SyntaxError(current.get(),
+ "Expected either a semicolon for an inline class "
+ "definition or a sequence of methods but instead got '" +
+ current->lexeme + "'.");
+ }
+}
+
+std::unique_ptr<Stmt> Parser::statement() {
+ if (match(TokenType::LeftBrace)) {
+ return scopedStmt();
+ } else if (match(TokenType::If)) {
+ return ifStmt();
+ } else if (match(TokenType::While)) {
+ return whileStmt();
+ } else if (match(TokenType::Loop)) {
+ return loopStmt();
+ } else if (match(TokenType::For)) {
+ return forStmt();
+ } else if (match(TokenType::Return)) {
+ return returnStmt();
+ }
+
+ return expressionStmt();
+}
+
+std::unique_ptr<Stmt> Parser::scopedStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+ return std::make_unique<ScopedStmt>(line, col, lexer->getFilename(),
+ stmtSequence());
+}
+
+std::unique_ptr<Stmt> Parser::ifStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+ std::unique_ptr<Expr> cond = expression();
+ std::unique_ptr<Stmt> trueBranch = statement();
+ std::unique_ptr<Stmt> falseBranch;
+
+ if (match(TokenType::Else)) {
+ falseBranch = statement();
+ }
+
+ return std::make_unique<IfStmt>(line, col, lexer->getFilename(), cond,
+ trueBranch, falseBranch);
+}
+
+std::unique_ptr<Stmt> Parser::whileStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ auto condition = expression();
+ auto body = statement();
+ return std::make_unique<WhileStmt>(line, col, lexer->getFilename(), condition,
+ body);
+}
+
+std::unique_ptr<Stmt> Parser::loopStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ std::unique_ptr<Expr> cond = std::make_unique<LiteralExpr>(
+ line, col, lexer->getFilename(),
+ 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());
+}
+
+std::unique_ptr<Stmt> Parser::forStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ consume("Expected '(' at the beginning of for statement but instead got '" +
+ current->lexeme + "'.",
+ TokenType::LeftParen);
+
+ std::unique_ptr<Stmt> initializer;
+ if (match(TokenType::Let)) {
+ initializer = varDecl();
+ } else if (!match(TokenType::Semicolon)) {
+ initializer = expressionStmt();
+ }
+
+ std::unique_ptr<Expr> cond;
+ if (!check(TokenType::Semicolon)) {
+ cond = expression();
+ }
+ consume("Expected ';' after loop condition but instead got '" +
+ current->lexeme + "'.",
+ TokenType::Semicolon);
+
+ std::unique_ptr<Expr> increment;
+ if (!check(TokenType::RightParen)) {
+ increment = expression();
+ }
+ consume("Expected ')' after loop clauses but instead got '" +
+ current->lexeme + "'.",
+ TokenType::RightParen);
+
+ std::unique_ptr<Stmt> body = statement();
+
+ if (!cond) {
+ cond = std::make_unique<LiteralExpr>(
+ line, col, lexer->getFilename(),
+ 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);
+}
+
+std::unique_ptr<Stmt> Parser::returnStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+ std::unique_ptr<Expr> value;
+
+ if (!check(TokenType::Semicolon)) {
+ value = expression();
+ }
+ CONSUME_SEMICOLON("return");
+
+ return std::make_unique<ReturnStmt>(line, col, lexer->getFilename(), value);
+}
+
+std::unique_ptr<Stmt> Parser::expressionStmt() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ auto expr =
+ std::make_unique<ExprStmt>(line, col, lexer->getFilename(), 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());
+ } else if (match(TokenType::Break)) {
+ int line = lexer->getLine(), col = lexer->getColumn();
+ return std::make_unique<ContinueExpr>(line, col, lexer->getFilename());
+ }
+
+ return assignment();
+}
+
+std::unique_ptr<Expr> Parser::assignment() {
+ std::unique_ptr<Expr> left = orExpr();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ 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);
+ } else if (auto get = dynamic_cast<GetFieldExpr *>(left.get())) {
+ return std::make_unique<SetFieldExpr>(line, col, lexer->getFilename(),
+ 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 + "'.",
+ TokenType::Colon);
+
+ return std::make_unique<TernaryExpr>(line, col, lexer->getFilename(), left,
+ trueExpr, assignment());
+ }
+
+ return left;
+}
+
+std::unique_ptr<Expr> Parser::orExpr() {
+ std::unique_ptr<Expr> left = andExpr();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ while (match(TokenType::Or)) {
+ std::shared_ptr<const Token> op = previous;
+
+ left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
+ andExpr(), op);
+ }
+
+ return left;
+}
+
+std::unique_ptr<Expr> Parser::andExpr() {
+ std::unique_ptr<Expr> left = equality();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ while (match(TokenType::And)) {
+ std::shared_ptr<const Token> op = previous;
+
+ left = std::make_unique<BinaryExpr>(line, col, lexer->getFilename(), left,
+ equality(), op);
+ }
+
+ return left;
+}
+
+std::unique_ptr<Expr> Parser::equality() {
+ std::unique_ptr<Expr> left = comparison();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ 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);
+ }
+
+ return left;
+}
+
+std::unique_ptr<Expr> Parser::comparison() {
+ std::unique_ptr<Expr> left = term();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ 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);
+ }
+
+ return left;
+}
+
+std::unique_ptr<Expr> Parser::term() {
+ std::unique_ptr<Expr> left = factor();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ 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);
+ }
+
+ return left;
+}
+
+std::unique_ptr<Expr> Parser::factor() {
+ std::unique_ptr<Expr> left = unary();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ 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);
+ }
+
+ 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 call();
+}
+
+std::unique_ptr<Expr> Parser::call() {
+ std::unique_ptr<Expr> expr = primary();
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ while (1) {
+ if (match(TokenType::LeftParen)) {
+ std::vector<std::unique_ptr<Expr>> args;
+ if (!check(TokenType::RightParen)) {
+ do {
+ args.emplace_back(expression());
+ } while (match(TokenType::Comma));
+ }
+
+ consume("Expected ')' after function call but instead got '" +
+ current->lexeme + "'.",
+ TokenType::RightParen);
+ expr = std::make_unique<FnCallExpr>(line, col, lexer->getFilename(), expr,
+ args);
+ } else if (match(TokenType::Dot)) {
+ std::shared_ptr<const Token> prop =
+ consume("Expected class property or method but instead got '" +
+ current->lexeme + "'.",
+ TokenType::Identifier);
+ expr = std::make_unique<GetFieldExpr>(line, col, lexer->getFilename(),
+ expr, prop);
+ } else {
+ break;
+ }
+ }
+
+ return expr;
+}
+
+std::unique_ptr<Expr> Parser::primary() {
+ int line = lexer->getLine(), col = lexer->getColumn();
+
+ 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);
+ } else if (match(TokenType::This)) {
+ return std::make_unique<ThisExpr>(line, col, lexer->getFilename());
+ } else if (match(TokenType::Identifier)) {
+ return std::make_unique<VariableExpr>(line, col, lexer->getFilename(),
+ 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);
+ } 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);
+ }
+
+ throw SyntaxError(current.get(), "'" + current->lexeme +
+ "' not a valid expression statement.");
+}
+
+void Parser::synchronize() {
+ advance();
+
+ while (!isAtEnd()) {
+ if (previous->type == TokenType::Semicolon) {
+ return;
+ }
+
+ switch (current->type) {
+ case TokenType::Class:
+ case TokenType::Fn:
+ case TokenType::Let:
+ case TokenType::While:
+ case TokenType::Loop:
+ case TokenType::For:
+ case TokenType::If:
+ case TokenType::Return:
+ return;
+ }
+
+ advance();
+ }
+}
+
+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;
+
+ if (isAtEnd()) {
+ 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);
+ }
+
+ std::shared_ptr<const Token> res = current;
+ advance();
+ return res;
+}
+
+std::vector<std::unique_ptr<Stmt>> Parser::stmtSequence() {
+ std::vector<std::unique_ptr<Stmt>> stmts;
+
+ while (!check(TokenType::RightBrace) && !isAtEnd()) {
+ try {
+ stmts.push_back(declaration());
+ } catch (SyntaxError &e) {
+ hadError = true;
+ std::cout << e.what();
+ synchronize();
+ }
+ }
+
+ consume("Expected '}' at scope block end but instead got '" +
+ current->lexeme + "'.",
+ TokenType::RightBrace);
+ return stmts;
+}
--- /dev/null
+#include "resolver.h"
+
+void Resolver::resolve(std::vector<std::unique_ptr<Stmt>> &stmts) {
+ for (auto &&stmt : stmts) {
+ try {
+ stmt->accept(this);
+ } catch (SyntaxError &e) {
+ std::cout << e.what();
+ hadError = true;
+ }
+ }
+}
+
+void Resolver::declare(const Token *name) {
+ if (scopes.empty()) {
+ return;
+ }
+
+ std::map<std::string, VarState> &scope = scopes.back();
+ if (scope.contains(name->lexeme)) {
+ throw SyntaxError(name, "Variable with this name already exists.");
+ }
+
+ scope.insert(std::make_pair(name->lexeme, VarState::Init));
+}
+
+void Resolver::define(const Token *name) {
+ if (scopes.empty()) {
+ return;
+ }
+
+ scopes.back().insert_or_assign(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)) {
+ interpreter->resolve(expr, scopes.size() - 1 - i);
+ return;
+ }
+ }
+}
+
+void Resolver::resolveLocal(Expr *expr, const std::string &name) {
+ for (int i = scopes.size() - 1; i >= 0; i--) {
+ if (scopes[i].contains(name)) {
+ interpreter->resolve(expr, scopes.size() - 1 - i);
+ return;
+ }
+ }
+}
+
+void Resolver::resolveFunction(FnStmt *fn, FunctionType::Type type) {
+ FunctionType::Type enclosingFn = currentFn;
+ currentFn = type;
+
+ beginScope();
+ for (auto &&arg : fn->args) {
+ declare(arg.get());
+ define(arg.get());
+ }
+
+ resolve(fn->body);
+ endScope();
+ currentFn = enclosingFn;
+}
+
+void Resolver::visitFnStmt(FnStmt *fn) {
+ declare(fn->name.get());
+ define(fn->name.get());
+ resolveFunction(fn, FunctionType::Function);
+}
+
+void Resolver::visitVarStmt(VarStmt *var) {
+ declare(var->name.get());
+ if (var->value) {
+ var->value->accept(this);
+ }
+
+ define(var->name.get());
+}
+
+void Resolver::visitClassStmt(ClassStmt *clas) {
+ declare(clas->name.get());
+ define(clas->name.get());
+
+ if (clas->superclass &&
+ clas->superclass->variable->lexeme == clas->name->lexeme) {
+ throw SyntaxError(clas, "A class can't inherit from itself.");
+ }
+
+ if (clas->superclass) {
+ currentClass = ClassType::Subclass;
+ clas->superclass->accept(this);
+
+ beginScope();
+ scopes.back().insert(std::make_pair("super", VarState::Ready));
+ } else {
+ currentClass = ClassType::None;
+ }
+
+ beginScope();
+ scopes.back().insert(std::make_pair("this", VarState::Ready));
+
+ for (auto &&stmt : clas->body) {
+ auto method = dynamic_cast<FnStmt *>(stmt.get());
+ if (method && method->name->lexeme == "init") {
+ resolveFunction(method, FunctionType::Initializer);
+ } else {
+ resolveFunction(method, FunctionType::Function);
+ }
+ }
+
+ endScope();
+
+ if (clas->superclass) {
+ endScope();
+ }
+}
+
+void Resolver::visitIfStmt(IfStmt *stmt) {
+ stmt->condition->accept(this);
+ stmt->trueBranch->accept(this);
+ if (stmt->falseBranch) {
+ stmt->falseBranch->accept(this);
+ }
+}
+
+void Resolver::visitWhileStmt(WhileStmt *loop) {
+ loop->condition->accept(this);
+ loops++;
+ loop->body->accept(this);
+ loops--;
+}
+
+void Resolver::visitForStmt(ForStmt *loop) {
+ beginScope();
+ loop->initializer->accept(this);
+ loop->condition->accept(this);
+ loop->increment->accept(this);
+ loops++;
+ loop->body->accept(this);
+ loops--;
+ endScope();
+}
+
+void Resolver::visitScopedStmt(ScopedStmt *block) {
+ beginScope();
+ resolve(block->body);
+ endScope();
+}
+
+void Resolver::visitExprStmt(ExprStmt *stmt) { stmt->expr->accept(this); }
+
+void Resolver::visitReturnStmt(ReturnStmt *ret) {
+ if (currentFn == FunctionType::None) {
+ throw SyntaxError(ret, "Can't return from top-level code");
+ } else if (ret->value) {
+ if (currentFn == FunctionType::Initializer) {
+ throw SyntaxError(ret, "Can't return a value from a class initializer.");
+ }
+
+ ret->value->accept(this);
+ }
+}
+
+LBPLType Resolver::visitBinaryExpr(BinaryExpr *expr) {
+ expr->left->accept(this);
+ expr->right->accept(this);
+ return nullptr;
+}
+
+LBPLType Resolver::visitBreakExpr(BreakExpr *expr) {
+ if (loops <= 0) {
+ throw SyntaxError(expr, "Can't break from outside of a loop.");
+ }
+ return nullptr;
+}
+
+LBPLType Resolver::visitContinueExpr(ContinueExpr *expr) {
+ if (loops <= 0) {
+ throw SyntaxError(expr, "Can't break from outside of a loop.");
+ }
+ return nullptr;
+}
+
+LBPLType Resolver::visitUnaryExpr(UnaryExpr *expr) {
+ expr->right->accept(this);
+ return nullptr;
+}
+
+LBPLType Resolver::visitLiteralExpr(LiteralExpr *) { return nullptr; }
+
+LBPLType Resolver::visitGroupExpr(GroupingExpr *expr) {
+ expr->expr->accept(this);
+ return nullptr;
+}
+
+LBPLType Resolver::visitSuperExpr(SuperExpr *expr) {
+ if (currentClass == ClassType::None) {
+ throw SyntaxError(expr, "Can't access 'super' from outside of class body.");
+ } else if (currentClass != ClassType::Subclass) {
+ throw SyntaxError(expr,
+ "Can't access 'super' in a class without superclass.");
+ }
+
+ resolveLocal(expr, "super");
+ return nullptr;
+}
+
+LBPLType Resolver::visitThisExpr(ThisExpr *expr) {
+ resolveLocal(expr, "this");
+ return nullptr;
+}
+
+LBPLType Resolver::visitCallExpr(FnCallExpr *expr) {
+ expr->callee->accept(this);
+
+ for (auto &&arg : expr->args) {
+ arg->accept(this);
+ }
+ return nullptr;
+}
+
+LBPLType Resolver::visitGetFieldExpr(GetFieldExpr *expr) {
+ expr->instance->accept(this);
+ return nullptr;
+}
+
+LBPLType Resolver::visitSetFieldExpr(SetFieldExpr *expr) {
+ expr->value->accept(this);
+ expr->instance->accept(this);
+ return nullptr;
+}
+
+LBPLType Resolver::visitTernaryExpr(TernaryExpr *expr) {
+ expr->condition->accept(this);
+ expr->trueBranch->accept(this);
+ expr->falseBranch->accept(this);
+ return nullptr;
+}
+
+LBPLType Resolver::visitVarExpr(VariableExpr *expr) {
+ if (!scopes.empty() && scopes.back().contains(expr->variable->lexeme) &&
+ scopes.back().find(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.");
+ }
+
+ resolveLocal(expr, expr->variable.get());
+ return nullptr;
+}
+
+LBPLType Resolver::visitAssignExpr(AssignExpr *expr) {
+ expr->value->accept(this);
+ resolveLocal(expr, expr->variable.get());
+ return nullptr;
+}
--- /dev/null
+#include "runtime_error.h"
+
+std::string RuntimeError::what() {
+ std::stringstream ss(msg);
+ std::string lineInfo = "\033[1;31m[line " + std::to_string(line) + ":" +
+ std::to_string(column) + " in " + filename +
+ "]\033[0m: ";
+ std::string line, res = lineInfo;
+ std::string indentation(lineInfo.length() - 11, ' ');
+ bool firstLine = true;
+
+ while (std::getline(ss, line)) {
+ if (firstLine) {
+ res += line + "\n";
+ firstLine = false;
+ } else {
+ res += indentation + line + "\n";
+ }
+ }
+
+ return res;
+}
--- /dev/null
+#include "syntax_error.h"
+
+std::string SyntaxError::what() {
+ std::stringstream ss(msg);
+ std::string lineInfo = "\033[1;31m[line " + std::to_string(line) + ":" +
+ std::to_string(column) + " in " + filename +
+ "]\033[0m: ";
+ std::string line, res = lineInfo;
+ std::string indentation(lineInfo.length() - 11, ' ');
+ bool firstLine = true;
+
+ while (std::getline(ss, line)) {
+ if (firstLine) {
+ res += line + "\n";
+ firstLine = false;
+ } else {
+ res += indentation + line + "\n";
+ }
+ }
+
+ return res;
+}