From d87e8402696f06f5ed59a480074b7e37372bca8b Mon Sep 17 00:00:00 2001 From: Amanuel Bogale Date: Tue, 2 Jul 2024 10:09:55 -0700 Subject: [PATCH] solved the recursion problem, but still not right answer --- examples/internal/test.rf | 19 +++++++++----- include/ast/decl.hh | 20 ++++++++++++-- include/ast/eval.hh | 4 +++ include/ast/expr.hh | 2 +- include/ast/grmr.hh | 4 +-- include/ast/parser.hh | 2 +- include/ast/stmt.hh | 10 ++++++- include/reader/reader.hh | 2 +- include/scanner/tokens.hh | 1 + lib/ast/eval.cc | 55 +++++++++++++++++++++------------------ lib/ast/parser.cc | 35 +++++++++++++++++++------ lib/scanner/tokens.cc | 5 ++-- 12 files changed, 109 insertions(+), 50 deletions(-) diff --git a/examples/internal/test.rf b/examples/internal/test.rf index ef4e198..03b38f2 100644 --- a/examples/internal/test.rf +++ b/examples/internal/test.rf @@ -1,8 +1,15 @@ -for(mut a = 0; a < 10; a = a + 1) { - print(a); -} +//for(mut a = 0; a < 10; a = a + 1) { +// print(a); +//} -fun test() { - print("First Function"); +//fun test() { +// print("Inside"); +// return 10; +//} +//print(test()); +fun fib(n) { + if (n <= 1) return n; + return fib(n - 2) + fib(n - 1); } -test(); \ No newline at end of file + +print(fib(2)); \ No newline at end of file diff --git a/include/ast/decl.hh b/include/ast/decl.hh index 0b66ba7..f21c4d9 100644 --- a/include/ast/decl.hh +++ b/include/ast/decl.hh @@ -36,7 +36,15 @@ namespace rift { public: DeclStmt(std::unique_ptr stmt) : stmt(std::move(stmt)) {}; - Tokens accept(const Visitor &visitor) const override { return visitor.visit_decl_stmt(*this); } + Tokens accept(const Visitor &visitor) const override { + try { + auto test = visitor.visit_decl_stmt(*this); + return test; + } catch(...) { + std::cout << "visit_decl_accept" << std::endl; + throw; + } + } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" @@ -71,7 +79,15 @@ namespace rift ~Block() = default; vec_prog decls = nullptr; - Tokens accept(const Visitor &visitor) const override { return visitor.visit_block_stmt(*this); }; + Tokens accept(const Visitor &visitor) const override { + try { + auto test = visitor.visit_block_stmt(*this); + return test; + } catch(...) { + std::cout << "DGSGDS" << std::endl; + throw; + } + }; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" // must uncomment visit_printer in printer.hh diff --git a/include/ast/eval.hh b/include/ast/eval.hh index 5a30997..1b76f69 100644 --- a/include/ast/eval.hh +++ b/include/ast/eval.hh @@ -30,6 +30,7 @@ namespace rift { namespace ast { + static Token return_token = Token(TokenType::NIL, "", "", -1); class Eval { public: @@ -63,6 +64,9 @@ namespace rift StmtReturnException(Token tok): tok(tok) {}; ~StmtReturnException() = default; Token tok; + const char* what() const noexcept override { + return "Statement Return Exception"; + } }; } } \ No newline at end of file diff --git a/include/ast/expr.hh b/include/ast/expr.hh index a487703..b474ce1 100644 --- a/include/ast/expr.hh +++ b/include/ast/expr.hh @@ -58,7 +58,7 @@ namespace rift #pragma mark - Concrete Expressions - using Exprs = std::vector>; + using Exprs = std::unordered_map>; class Call : public Expr { public: diff --git a/include/ast/grmr.hh b/include/ast/grmr.hh index 1692c1b..a5ea495 100644 --- a/include/ast/grmr.hh +++ b/include/ast/grmr.hh @@ -41,7 +41,7 @@ namespace rift /// printStmt → "print" "(" expression ");" /// exprStmt → expression ";" /// ifStmt → "if" "(" expression ")" stmt|blk ( "elif" statment|blk )* ( "else" stmt|blk )? - /// returnStmt → "return" expression ";" + /// returnStmt → "return" (expression)? ";" /// ternary → expression "?" expression ":" expression ";" /// expression → equality ";" /// assignment → IDENTIFIER "=" assignment | equality @@ -109,7 +109,7 @@ namespace rift virtual Token visit_expr_stmt(const StmtExpr& stmt) const; virtual Token visit_print_stmt(const StmtPrint& stmt) const; virtual Token visit_if_stmt(const StmtIf& stmt) const; - [[noreturn]] virtual Token visit_return_stmt(const StmtReturn& stmt) const; + virtual Token visit_return_stmt(const StmtReturn& stmt) const; /* decl */ virtual Tokens visit_decl_stmt(const DeclStmt& decl) const; diff --git a/include/ast/parser.hh b/include/ast/parser.hh index 40aa910..5259616 100644 --- a/include/ast/parser.hh +++ b/include/ast/parser.hh @@ -104,7 +104,7 @@ namespace rift /// @example 1, 2, 3 Tokens params(); /// @example 1+1, "str", a - Exprs args(); + Exprs args(Tokens params); /// @note program std::unique_ptr program(); diff --git a/include/ast/stmt.hh b/include/ast/stmt.hh index e466d97..a3d7cdd 100644 --- a/include/ast/stmt.hh +++ b/include/ast/stmt.hh @@ -130,7 +130,15 @@ namespace rift ~StmtReturn() = default; std::unique_ptr expr; - Token accept(const Visitor &visitor) const override { return visitor.visit_return_stmt(*this); }; + Token accept(const Visitor &visitor) const override { + try { + auto ret = visitor.visit_return_stmt(*this); + return ret; + } catch(...) { + std::cout << "STMTT" << std::endl; + throw; + } + }; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" // must uncomment visit_printer in printer.hh diff --git a/include/reader/reader.hh b/include/reader/reader.hh index c63a650..9b06dfa 100644 --- a/include/reader/reader.hh +++ b/include/reader/reader.hh @@ -72,7 +72,7 @@ namespace rift /// @brief Scans a comment and advances the cursor - inline void scanComment() { while (peek('\n')) advance();}; + inline void scanComment() { while (!peek('\n')) advance(); advance(); }; /// @brief Consumes a T and advances the cursor (Error if not found) inline T consume (T expected, std::unique_ptr error) { if (peek(expected)) return advance(); diff --git a/include/scanner/tokens.hh b/include/scanner/tokens.hh index f106118..9927072 100644 --- a/include/scanner/tokens.hh +++ b/include/scanner/tokens.hh @@ -51,6 +51,7 @@ namespace rift LESS_EQUAL, SEPARATOR, WHITESPACE, + FAT_ARROW, // Operators OPERATOR, diff --git a/lib/ast/eval.cc b/lib/ast/eval.cc index 1f74c0d..63dcf38 100644 --- a/lib/ast/eval.cc +++ b/lib/ast/eval.cc @@ -64,7 +64,7 @@ namespace rift { Token val = expr.value; any literal; - Token res = env::getInstance(false).getEnv(castString(val)); + Token res = env::getInstance(false).getEnv(val.lexeme); if (val.type == TokenType::NIL) return Token(TokenType::NIL, "nil", nullptr, expr.value.line); @@ -106,7 +106,7 @@ namespace rift /* Function */ else if (res.type == TokenType::FUN) - return res; + return Token(TokenType::FUN, val.lexeme, literal, expr.value.line); else rift::error::runTimeError("Unknown literal type"); return Token(); @@ -220,7 +220,7 @@ namespace rift Token Visitor::visit_assign(const Assign& expr) const { - auto name = castString(expr.name); + auto name = expr.name.lexeme; env::getInstance(false).setEnv(name, expr.value->accept(*this), expr.name.type == TokenType::C_IDENTIFIER); return env::getInstance(false).getEnv(name); } @@ -272,24 +272,22 @@ namespace rift Token Visitor::visit_call(const Call& expr) const { auto name = expr.name->accept(*this); + if (name.type == TokenType::NIL) rift::error::runTimeError("Undefined function '" + name.lexeme + "'"); - - std::vector args; - for (const auto& arg: expr.args) { - args.push_back(arg->accept(*this)); + + // map arguments to parameters + for (const auto& arg : expr.args) { + env::getInstance(false).setEnv(arg.first, arg.second->accept(*this), false); } - // exception for return stmt, then return that - // if no exception occurs return nil auto blk = std::any_cast(name.literal); - try { - auto res = blk->accept(*this); - return Token(); - } catch (const StmtReturnException& e) { - return e.tok; - } + auto res = blk->accept(*this); + + auto tmp = return_token; + return_token = Token(TokenType::NIL, "", "", -1); + return tmp; } #pragma mark - Stmt Visitors @@ -313,9 +311,9 @@ namespace rift { auto if_stmt = stmt.if_stmt; // if stmt - auto expr = std::move(if_stmt->expr); - if (expr == nullptr) rift::error::runTimeError("If statement expression should not be null"); - auto expr_tok = expr->accept(*this); + // auto expr = std::move(if_stmt->expr); + // if (expr == nullptr) rift::error::runTimeError("If statement expression should not be null"); + auto expr_tok = if_stmt->expr->accept(*this); if (truthy(expr_tok)) { if (if_stmt->blk != nullptr) if_stmt->blk->accept(*this); @@ -346,11 +344,10 @@ namespace rift return Token(); } - [[noreturn]] Token Visitor::visit_return_stmt(const StmtReturn& stmt) const + Token Visitor::visit_return_stmt(const StmtReturn& stmt) const { - auto val = stmt.expr->accept(*this); - // must throw exception to return the value - throw StmtReturnException(val); + return_token = stmt.expr->accept(*this); + return return_token; } #pragma mark - Program / Block Visitor @@ -361,8 +358,9 @@ namespace rift env::addChild(false); // add scope for (auto it=block.decls->begin(); it!=block.decls->end(); it++) { - auto its = (*it)->accept(*this); - toks.insert(toks.end(), its.begin(), its.end()); + if (return_token.line != -1) break; // -1 = no return + auto its = (*it)->accept(*this); + toks.insert(toks.end(), its.begin(), its.end()); } env::removeChild(false); // remove scope @@ -384,7 +382,12 @@ namespace rift Tokens Visitor::visit_decl_stmt(const DeclStmt& decl) const { std::vector toks; - toks.push_back(decl.stmt->accept(*this)); + try { + toks.push_back(decl.stmt->accept(*this)); + } catch(...) { + std::cout << "visit_decl" << std::endl; + throw; + } return toks; } @@ -407,7 +410,7 @@ namespace rift auto name = decl.func->name; auto params = decl.func->params; - if (env::getInstance(false).getEnv(castString(name)).type != TokenType::NIL) + if (env::getInstance(false).getEnv(name.lexeme).type != TokenType::NIL) rift::error::runTimeError("Function '" + name.lexeme + "' already defined"); if (decl.func->blk != nullptr) { diff --git a/lib/ast/parser.cc b/lib/ast/parser.cc index 9b276c7..f40f663 100644 --- a/lib/ast/parser.cc +++ b/lib/ast/parser.cc @@ -70,10 +70,18 @@ namespace rift // since that's the only way to verify between func test() {} and test(); // note the "test()"" if (peekPrev().type == TokenType::IDENTIFIER && peek() == Token(TokenType::LEFT_PAREN)) { + // get function from token, and grab its paramaters so I can plug them in with args + auto idt = peekPrev(); + auto func = env::getInstance(true).getEnv(idt.lexeme); + if (func.type != TokenType::FUN) rift::error::report(line, "call", "Expected function", idt, ParserException("Expected function")); + Tokens params = std::any_cast(func.literal); + consume(Token(TokenType::LEFT_PAREN)); - auto arg = args(); + auto arg = args(params); consume(Token(TokenType::RIGHT_PAREN)); - consume(Token(TokenType::SEMICOLON)); + // another dillema, how do i handle return 3; + // do I handle it here or in the return stmt, I choose later + // consume(Token(TokenType::SEMICOLON)); return std::unique_ptr(new Call(std::move(expr), std::move(arg))); } @@ -242,7 +250,7 @@ namespace rift auto blk = block(); if_stmt->blk = std::move(blk); } else { - std::unique_ptr stmt = ret_stmt(); + auto stmt = ret_stmt(); if_stmt->stmt = std::move(stmt); } ret->if_stmt = if_stmt; @@ -431,12 +439,16 @@ namespace rift consume(Token(TokenType::LEFT_PAREN, "(", "", line), std::unique_ptr(new ParserException("Expected '(' after function name"))); ret->params = params(); consume(Token(TokenType::RIGHT_PAREN, ")", "", line), std::unique_ptr(new ParserException("Expected ')' after function params"))); - + // give the params (usefull for the call operator) + env::getInstance(true).setEnv(idt.lexeme, Token(TokenType::FUN, idt.lexeme, ret->params, idt.line), false); + if(match({Token(TokenType::LEFT_BRACE, "{", "", line)})) { ret->blk = std::move(block()); - } else { + } else if(match({Token(TokenType::FAT_ARROW, "=>", "", line)})) { // TODO: allow stmt to emulate lambdas rift::error::report(line, "function", "Lambdas not implemented yet", peek(), ParserException("Lambdas not implemented yet")); + } else { + consume(Token(TokenType::SEMICOLON, ";", "", line), std::unique_ptr(new ParserException("Expected ';' after function declaration"))); } return ret; @@ -446,19 +458,26 @@ namespace rift Tokens Parser::params() { Tokens toks = {}; - while(peek() == Token(TokenType::IDENTIFIER) || peek() == Token(TokenType::C_IDENTIFIER)) { + while(peek().type != TokenType::RIGHT_PAREN) { toks.push_back(consume_va({Token(TokenType::IDENTIFIER), Token(TokenType::C_IDENTIFIER)}, std::unique_ptr(new ParserException("Expected parameter name")))); if (!consume(Token(TokenType::COMMA, ",", "", line))) break; } return toks; } - Exprs Parser::args() + Exprs Parser::args(Tokens params) { Exprs exprs = {}; + int idx = 0; while(peek().type != TokenType::RIGHT_PAREN) { auto exp = expression(); - exprs.emplace_back(std::move(exp)); + if (idx >= params.size()) + rift::error::report(line, "args", "Too many arguments", peek(), ParserException("Too many arguments")); + if (exp == nullptr) + rift::error::report(line, "args", "Expected expression", peek(), ParserException("Expected expression")); + + exprs.insert({params[idx].lexeme, std::move(exp)}); + idx++; } return exprs; } diff --git a/lib/scanner/tokens.cc b/lib/scanner/tokens.cc index b443bdc..4048766 100644 --- a/lib/scanner/tokens.cc +++ b/lib/scanner/tokens.cc @@ -71,6 +71,7 @@ std::string Token::convertTypeString(TokenType type) { case WHILE: return "WHILE"; case C_IDENTIFIER: return "C_IDENTIFIER"; case IGNORE: return "IGNORE"; + case FAT_ARROW: return "FAT_ARROW"; case EOFF: return "EOFF"; } } @@ -170,9 +171,9 @@ std::any Token::getLiteral() const return std::any{std::string(lexeme)}; } else if (type == IDENTIFIER || type == C_IDENTIFIER) { return std::any{std::string(lexeme)}; - } else if (type == NUMERICLITERAL) { + }/* else if (type == NUMERICLITERAL) { return std::any{std::stod(lexeme)}; - } else if (type == TRUE) { + }*/ else if (type == TRUE) { return std::any{true}; } else if (type == FALSE) { return std::any{false};