diff --git a/include/ast/decl.hh b/include/ast/decl.hh index 1739948..332c715 100644 --- a/include/ast/decl.hh +++ b/include/ast/decl.hh @@ -50,7 +50,7 @@ namespace rift class DeclVar: public Decl { public: - DeclVar(const Token&identifier): identifier(identifier), expr(nullptr) {}; + DeclVar(const Token &identifier): identifier(identifier), expr(nullptr) {}; DeclVar(const Token &identifier, std::unique_ptr expr): identifier(identifier), expr(std::move(expr)) {}; Tokens accept(const Visitor &visitor) const override { return visitor.visit_decl_var(*this); } diff --git a/include/ast/env.hh b/include/ast/env.hh index 7d73dcb..61f41ec 100644 --- a/include/ast/env.hh +++ b/include/ast/env.hh @@ -17,6 +17,7 @@ // #include #include #include +#include #include using Token = rift::scanner::Token; @@ -71,12 +72,13 @@ namespace rift Environment(Environment *child) : child(child) {} ~Environment() = default; Token getEnv(const str_t& name) const; - void setEnv(const str_t& name, const Token& value); + void setEnv(const str_t& name, const Token& value, bool is_const); void printState(); Environment *child; protected: // absl::flat_hash_map values; std::unordered_map values = {}; + std::unordered_set const_keys = {}; }; } } \ No newline at end of file diff --git a/include/ast/parser.hh b/include/ast/parser.hh index f01b507..9887a24 100644 --- a/include/ast/parser.hh +++ b/include/ast/parser.hh @@ -85,8 +85,8 @@ namespace rift /// @note rules in order of precedence /// @example var x = 1; std::unique_ptr declaration_statement(); - /// @example var x = 1; - std::unique_ptr declaration_variable(); + /// @example mut x = 1; mut! x = 5; + std::unique_ptr declaration_variable(bool mut); /// @example while(true) print("hi"); std::unique_ptr for_(); @@ -100,6 +100,8 @@ namespace rift void synchronize(); /// @brief returns any statements that might be executed std::unique_ptr ret_stmt(); + /// @brief returns any declarations that might be executed + vec_prog ret_decl(); }; class ParserException : public ReaderException diff --git a/include/scanner/scanner.hh b/include/scanner/scanner.hh index a78caa5..1ecdc66 100644 --- a/include/scanner/scanner.hh +++ b/include/scanner/scanner.hh @@ -21,7 +21,7 @@ #include // #include -#include +#include #include using namespace rift::reader; @@ -37,7 +37,7 @@ namespace rift { std::shared_ptr> source; std::vector tokens; - std::unordered_map keywords; + std::map > keywords; Scanner(std::shared_ptr> source); ~Scanner(){} diff --git a/include/scanner/tokens.hh b/include/scanner/tokens.hh index d46b785..98c6500 100644 --- a/include/scanner/tokens.hh +++ b/include/scanner/tokens.hh @@ -62,6 +62,7 @@ namespace rift // Literals IDENTIFIER, + C_IDENTIFIER, STRINGLITERAL, NUMERICLITERAL, @@ -80,6 +81,7 @@ namespace rift THIS, TRUE, VAR, + CONST, WHILE, IGNORE, @@ -130,7 +132,7 @@ namespace rift << ", Line=" << token.line << ")"*/; return os; } - bool operator==(const Token &token); + bool operator==(const Token &token) const; std::any getLiteral() const; }; diff --git a/lib/ast/env.cc b/lib/ast/env.cc index 94d8599..79d9401 100644 --- a/lib/ast/env.cc +++ b/lib/ast/env.cc @@ -31,12 +31,17 @@ namespace rift return values.at(name); } - void Environment::setEnv(const str_t& name, const Token& value) + void Environment::setEnv(const str_t& name, const Token& value, bool is_const) { if (!values.contains(name) && child != nullptr) { - child->setEnv(name, value); + child->setEnv(name, value, is_const); } else { - values[name] = value; + if (values.contains(name) && const_keys.contains(name)) { + error::report(0, "Environment", "Cannot reassign a constant variable", values.at(name), std::exception()); + } else { + values[name] = value; + if (is_const) const_keys.insert(name); + } } } diff --git a/lib/ast/eval.cc b/lib/ast/eval.cc index 3a10c94..216f5c9 100644 --- a/lib/ast/eval.cc +++ b/lib/ast/eval.cc @@ -112,7 +112,7 @@ namespace rift Token Visitor::visit_assign(const Assign& expr) const { auto name = castString(expr.name); - env::getInstance().setEnv(name, expr.value->accept(*this)); + env::getInstance().setEnv(name, expr.value->accept(*this), expr.name.type == TokenType::C_IDENTIFIER); return env::getInstance().getEnv(name);; } @@ -363,7 +363,7 @@ namespace rift toks.push_back(decl.expr->accept(*this)); } else { auto niltok = Token(TokenType::NIL, "null", nullptr, decl.identifier.line); - env::getInstance().setEnv(decl.identifier.lexeme, niltok); + env::getInstance().setEnv(decl.identifier.lexeme, niltok, decl.identifier.type == TokenType::C_IDENTIFIER); toks.push_back(niltok); } return toks; diff --git a/lib/ast/parser.cc b/lib/ast/parser.cc index 1eee5fb..7fc2883 100644 --- a/lib/ast/parser.cc +++ b/lib/ast/parser.cc @@ -295,9 +295,10 @@ namespace rift return std::make_unique(std::move(stmt)); } - std::unique_ptr Parser::declaration_variable() + std::unique_ptr Parser::declaration_variable(bool mut) { // make sure there is an identifier + auto tok_t = mut ? TokenType::IDENTIFIER : TokenType::C_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 @@ -305,18 +306,20 @@ namespace rift /// this also checks if any outer block has already declared this variable if (env::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")); - env::getInstance().setEnv(castString(idt), idt); + env::getInstance().setEnv(castString(idt), idt, idt.type == TokenType::C_IDENTIFIER); if(peekNext() == Token(TokenType::EQUAL, "=", "", line)) { auto expr = assignment(); consume(Token(TokenType::SEMICOLON, ";", "", line), std::unique_ptr(new ParserException("Expected ';' after variable assignment"))); + idt.type = tok_t; return std::make_unique(idt, std::move(expr)); + } else if (!mut) { + rift::error::report(line, "declaration_variable", "🛑 Constants must be defined", idt, ParserException("Constants must be defined")); } idt = consume(Token(TokenType::IDENTIFIER, "", "", line), std::unique_ptr(new ParserException("Expected variable name"))); consume(Token(TokenType::SEMICOLON, ";", "", line), std::unique_ptr(new ParserException("Expected ';' after variable declaration"))); - // return std::make_unique(idt, std::unique_ptr(new Literal(Token(TokenType::NIL, "nil", "", line)))); - return std::make_unique(idt); + return std::unique_ptr(new DeclVar(Token(tok_t, idt.lexeme, idt.literal, idt.line))); } std::unique_ptr Parser::for_() @@ -326,7 +329,9 @@ namespace rift // first ; if (match({Token(TokenType::VAR, "", "", line)})) { - _for->decl = std::move(declaration_variable()); + _for->decl = std::move(declaration_variable(true)); + } else if (match({Token(TokenType::CONST, "", "", line)})) { + _for->decl = std::move(declaration_variable(false)); } else if(peek(Token(TokenType::IDENTIFIER, "", "", line))) { _for->stmt_l = std::move(ret_stmt()); } @@ -339,9 +344,8 @@ namespace rift consume(Token(TokenType::SEMICOLON, ";", "", line), std::unique_ptr(new ParserException("Expected ';' after for second statement"))); // third ; - if(peek(Token(TokenType::IDENTIFIER, "", "", line))) { + if(peek(Token(TokenType::IDENTIFIER, "", "", line))) _for->stmt_r = std::move(ret_stmt()); - } consume(Token(TokenType::RIGHT_PAREN, ")", "", line), std::unique_ptr(new ParserException("Expected ')' after for"))); if (match({Token(TokenType::LEFT_BRACE, "{", "", line)})) { @@ -355,21 +359,34 @@ namespace rift #pragma mark - Program / Block Parsing + vec_prog Parser::ret_decl() + { + vec_prog decls = std::make_unique>>(); + if (consume (Token(TokenType::VAR, "", "", line))) { + auto test = declaration_variable(true); + decls->emplace_back(std::move(test)); + } else if (consume (Token(TokenType::CONST, "", "", line))) { + auto test = declaration_variable(false); + decls->emplace_back(std::move(test)); + } else if (consume (Token(TokenType::FOR, "", "", line))) { + decls->emplace_back(for_()); + } else if(match({Token(TokenType::LEFT_BRACE, "{", "", line)})) { + auto inner_decls = std::move(block()->decls); + decls->insert(decls->end(), std::make_move_iterator(inner_decls->begin()), std::make_move_iterator(inner_decls->end())); + } else { + decls->emplace_back(declaration_statement()); + } + return decls; + } + std::unique_ptr Parser::block() { vec_prog decls = std::make_unique>>(); env::addChild(); while (!atEnd() && !peek(Token(TokenType::RIGHT_BRACE, "}", "", line))) { - if (match({Token(TokenType::LEFT_BRACE, "{", "", line)})) { - auto inner_decls = std::move(block()->decls); - decls->insert(decls->end(), std::make_move_iterator(inner_decls->begin()), std::make_move_iterator(inner_decls->end())); - } else if (consume (Token(TokenType::VAR, "", "", line))) { - auto decl = declaration_variable(); - decls->push_back(std::move(decl)); - } else { - decls->push_back(declaration_statement()); - } + auto inner = ret_decl(); + decls->insert(decls->end(), std::make_move_iterator(inner->begin()), std::make_move_iterator(inner->end())); } env::removeChild(); @@ -384,16 +401,8 @@ namespace rift vec_prog decls = std::make_unique>>(); while (!atEnd()) { - if (consume (Token(TokenType::VAR, "", "", line))) { - auto test = declaration_variable(); - decls->emplace_back(std::move(test)); // GIVING ME UB - } else if (consume (Token(TokenType::FOR, "", "", line))) { - decls->push_back(for_()); - } else if(match({Token(TokenType::LEFT_BRACE, "{", "", line)})) { - decls->push_back(block()); - } else { - decls->push_back(declaration_statement()); - } + auto inner = ret_decl(); + decls->insert(decls->end(), std::make_move_iterator(inner->begin()), std::make_move_iterator(inner->end())); } return std::unique_ptr(new Program(std::move(decls))); diff --git a/lib/scanner/scanner.cc b/lib/scanner/scanner.cc index a56876a..553a8d9 100644 --- a/lib/scanner/scanner.cc +++ b/lib/scanner/scanner.cc @@ -32,7 +32,7 @@ namespace rift this->source = source; this->tokens = std::vector(); - keywords = std::unordered_map(); + keywords = {}; keywords["and"] = Type::LOG_AND; keywords["class"] = Type::CLASS; keywords["false"] = Type::FALSE; @@ -48,6 +48,7 @@ namespace rift keywords["super"] = Type::SUPER; keywords["this"] = Type::THIS; keywords["true"] = Type::TRUE; + keywords["mut!"] = Type::CONST; keywords["mut"] = Type::VAR; keywords["while"] = Type::WHILE; } diff --git a/lib/scanner/tokens.cc b/lib/scanner/tokens.cc index 7411f2e..eaef7c0 100644 --- a/lib/scanner/tokens.cc +++ b/lib/scanner/tokens.cc @@ -67,7 +67,9 @@ std::string Token::convertTypeString(TokenType type) { case THIS: return "THIS"; case TRUE: return "TRUE"; case VAR: return "VAR"; + case CONST: return "CONST"; case WHILE: return "WHILE"; + case C_IDENTIFIER: return "C_IDENTIFIER"; case IGNORE: return "IGNORE"; case EOFF: return "EOFF"; } @@ -209,7 +211,7 @@ std::any Token::getLiteral() const #pragma mark - Operators -bool Token::operator==(const Token &token) +bool Token::operator==(const Token &token) const { return this->type == token.type; } \ No newline at end of file