From ee3d82dae6a68b6cbf04583db5859d091eb15112 Mon Sep 17 00:00:00 2001 From: Aman Date: Tue, 25 Jun 2024 13:05:25 -0700 Subject: [PATCH] var done --- CMakeLists.txt | 4 +++- include/ast/decl.hh | 5 ++--- include/ast/env.hh | 15 +++++++++++---- lib/CMakeLists.txt | 3 +-- lib/ast/env.cc | 11 +++++++---- lib/ast/eval.cc | 15 ++++++--------- lib/ast/parser.cc | 31 ++++++++++++++++++++++++------- lib/error/error.cc | 1 + lib/scanner/scanner.cc | 1 + lib/scanner/tokens.cc | 3 +++ 10 files changed, 59 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ffba39..9941990 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,8 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) include_directories(${gmock_SOURCE_DIR}/include ${gmock_SOURCE_DIR}) # Google {Abseil} -add_subdirectory(external/abseil) +# add_compile_definitions(ABSL_USE_GOOGLE_DEFAULT_NAMESPACE) +# add_subdirectory(external/abseil) +# find_package(absl REQUIRED) # include_directories(${abseil_SOURCE_DIR}) # target_link_libraries(riftlang absl::base absl::synchronization absl::strings) \ No newline at end of file diff --git a/include/ast/decl.hh b/include/ast/decl.hh index 70db74c..cddb017 100644 --- a/include/ast/decl.hh +++ b/include/ast/decl.hh @@ -30,8 +30,6 @@ namespace rift friend class Visitor; friend class DeclStmt; friend class DeclVar; - protected: - rift::ast::Environment env = {}; }; class DeclStmt: public Decl @@ -52,7 +50,7 @@ namespace rift class DeclVar: public Decl { public: - DeclVar(const Token &identifier): identifier(identifier) {}; + DeclVar(const Token &identifier, std::unique_ptr expr): identifier(identifier), expr(std::move(expr)) {}; Token accept(const Visitor &visitor) const override { return visitor.visit_decl_var(*this); } #pragma clang diagnostic push @@ -62,6 +60,7 @@ namespace rift #pragma clang diagnostic pop const Token& identifier; + std::unique_ptr expr; }; } } \ No newline at end of file diff --git a/include/ast/env.hh b/include/ast/env.hh index a98f097..b7f3023 100644 --- a/include/ast/env.hh +++ b/include/ast/env.hh @@ -16,7 +16,7 @@ // #include "../../external/abseil/absl/container/flat_hash_map.h" // #include #include -#include +#include #include using Token = rift::scanner::Token; @@ -30,14 +30,21 @@ namespace rift class Environment { public: - Environment() = default; - ~Environment() = default; - + static Environment& getInstance() { + static Environment instance; + return instance; + } Token getEnv(const str_t& name) const; void setEnv(const str_t& name, const Token& value); protected: // absl::flat_hash_map values; std::unordered_map values = {}; + Environment(Environment &other) = delete; + Environment &operator=(const Environment &other) = delete; + + private: + Environment() = default; + ~Environment() = default; }; } } \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 50e6634..47d7e30 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -43,11 +43,10 @@ add_executable( ${SOURCES} ) -find_package(absl REQUIRED) target_compile_options(riftlang PRIVATE -Wno-gcc-compat) target_compile_definitions(riftlang PRIVATE ABSL_USES_STD_ANY=1) -target_link_libraries(riftlang absl::base absl::strings absl::hash absl::algorithm absl::memory absl::flat_hash_map absl::container_common absl::container_memory) +# target_link_libraries(riftlang absl::base absl::strings absl::hash absl::algorithm absl::memory absl::flat_hash_map absl::container_common absl::container_memory) add_library(riftlib STATIC ${SOURCES}) diff --git a/lib/ast/env.cc b/lib/ast/env.cc index 7e85d21..af4134c 100644 --- a/lib/ast/env.cc +++ b/lib/ast/env.cc @@ -21,17 +21,20 @@ namespace rift { Token Environment::getEnv(const str_t& name) const { + // if(tok == Token()) + // rift::error::runTimeError("🛑 Undefined variable '" + name + "'"); + // } + for (const auto& [key, value] : values) { + std::cout << key << " => " << value.to_string() << std::endl; + } if (!values.contains(name)) { return Token(); } - return values.at("env"); + return values.at(name); } void Environment::setEnv(const str_t& name, const Token& value) { - if (values.contains(name)) { - std::cout << "🟡 [line " << value.line << "] Warn: Variable '" << name << "' already defined." << std::endl; - } values[name] = value; } } diff --git a/lib/ast/eval.cc b/lib/ast/eval.cc index 0dd6683..1b70950 100644 --- a/lib/ast/eval.cc +++ b/lib/ast/eval.cc @@ -16,6 +16,7 @@ #include #include #include +#include namespace rift { @@ -59,10 +60,10 @@ namespace rift Token Visitor::visit_assign(const Assign& expr) const { - Token val = expr.value->accept(*this); auto name = castString(expr.name); - expr.env.assign(name, val); - return val; + auto tok = rift::ast::Environment::getInstance().getEnv(name); + rift::ast::Environment::getInstance().setEnv(name, expr.value->accept(*this)); + return tok; } Token Visitor::visit_binary(const Binary& expr) const @@ -243,12 +244,8 @@ namespace rift Token Visitor::visit_decl_var(const DeclVar& decl) const { - auto name = castString(decl.identifier); - auto tok = decl.env.getEnv(name); - if(tok == Token()) - rift::error::runTimeError("🛑 Undefined variable '" + name + "'"); - return tok; + // check performed in parser, undefined variables are CT errors + return decl.expr->accept(*this); } - } } \ No newline at end of file diff --git a/lib/ast/parser.cc b/lib/ast/parser.cc index a1c4371..4cc1487 100644 --- a/lib/ast/parser.cc +++ b/lib/ast/parser.cc @@ -17,6 +17,8 @@ #include #include #include +#include +#include using namespace rift::scanner; @@ -52,8 +54,14 @@ namespace rift return std::unique_ptr(new Literal(peekPrev(1))); if (match({Token(TokenType::STRINGLITERAL, "", "", line)})) return std::unique_ptr(new Literal(Token(peekPrev(1)))); - if (match({Token(TokenType::IDENTIFIER, "", "", line)})) + if (match({Token(TokenType::IDENTIFIER, "", "", line)})) { + /// @note rhs identifier undeclared + auto idt = peekPrev(1); + if (rift::ast::Environment::getInstance().getEnv(castString(idt)) == Token()) { + rift::error::report(line, "primary", "🛑 Undefined variable '" + castString(idt) + "' at line: " + castNumberString(idt.line), idt, ParserException("Undefined variable '" + castString(idt) + "'")); + } return std::unique_ptr(new Literal(Token(peekPrev(1)))); + } if (match({Token(TokenType::LEFT_PAREN, "(", "", line)})) { auto expr = expression(); @@ -150,6 +158,10 @@ namespace rift consume(Token(TokenType::EQUAL, "=", "", line), std::unique_ptr(new ParserException("Expected '=' after variable name"))); auto expr = assignment(); if (expr == nullptr) rift::error::report(line, "assignment", "Expected expression after variable name", peekPrev(), ParserException("Expected expression after variable name")); + + // assignemnt operator expects lhs to be already declared + if (rift::ast::Environment::getInstance().getEnv(castString(idt)) == Token()) + rift::error::report(line, "assignment", "🛑 Undefined variable '" + castString(idt) + "' at line: " + castNumberString(idt.line), idt, ParserException("Undefined variable '" + castString(idt) + "'")); return std::unique_ptr(new Assign(idt, std::move(expr))); } @@ -195,12 +207,17 @@ namespace rift std::unique_ptr Parser::declaration_variable() { - // auto idt = consume(Token(TokenType::IDENTIFIER, "", "", line), std::unique_ptr(new ParserException("Expected variable name"))); - // consume(Token(TokenType::EQUAL, "=", "", line), std::unique_ptr(new ParserException("Expected '=' after variable name"))); - // consume(Token(TokenType::SEMICOLON, ";", "", line), std::unique_ptr(new ParserException("Expected ';' after variable declaration"))); - // auto val = consume(Token(TokenType::NUMERICLITERAL, "", "", line), std::unique_ptr(new ParserException("Expected number after variable declaration"))); - // return std::make_unique(idt); - return nullptr; + // make sure there is an identifier + auto idt = consume(Token(TokenType::IDENTIFIER, "", "", line), std::unique_ptr(new ParserException("Expected variable name"))); + prevance(); + // make sure the identifier is not already declared + if (rift::ast::Environment::getInstance().getEnv(castString(idt)) != Token()) + rift::error::report(line, "declaration_variable", "🛑 Variable '" + castString(idt) + "' already declared at line: " + castNumberString(idt.line), idt, ParserException("Variable '" + castString(idt) + "' already declared")); + rift::ast::Environment::getInstance().setEnv(castString(idt), Token(TokenType::NIL, "nil", "", line)); + + auto expr = assignment(); + consume(Token(TokenType::SEMICOLON, ";", "", line), std::unique_ptr(new ParserException("Expected ';' after variable assignment"))); + return std::make_unique(idt, std::move(expr)); } #pragma mark - Program Parsing diff --git a/lib/error/error.cc b/lib/error/error.cc index 314387d..18908c1 100644 --- a/lib/error/error.cc +++ b/lib/error/error.cc @@ -36,6 +36,7 @@ namespace rift { std::cout << "⛔️ Runtime Error: " << msg << std::endl; runtimeErrorOccured = true; + exit(1); } } } \ No newline at end of file diff --git a/lib/scanner/scanner.cc b/lib/scanner/scanner.cc index d6fe97c..aee3168 100644 --- a/lib/scanner/scanner.cc +++ b/lib/scanner/scanner.cc @@ -96,6 +96,7 @@ namespace rift void Scanner::identifier() { while (isAlphaNumeric(advance())); std::string text = std::string(source->begin()+start, source->begin()+curr); + text.erase(std::remove_if(text.begin(), text.end(), ::isspace), text.end()); if (keywords.find(text)!= keywords.end()) { addToken(keywords.at(text)); } else { diff --git a/lib/scanner/tokens.cc b/lib/scanner/tokens.cc index c759ff5..c9cd082 100644 --- a/lib/scanner/tokens.cc +++ b/lib/scanner/tokens.cc @@ -160,7 +160,10 @@ std::any Token::getLiteral() const { if (type == STRINGLITERAL) { return std::any{std::string(lexeme)}; + } else if (type == IDENTIFIER) { + return std::any{std::string(lexeme)}; } + if (isInteger(lexeme.c_str())) return std::any{std::stoi(lexeme.c_str())}; if (isUnsignedInteger(lexeme.c_str()))