diff --git a/include/ast/BlockNode.h b/include/ast/BlockNode.h index d1ec053029b16dd18ca585d020f14667c606ecb9..7879f9038b9a7464818f1149128014c0c29f0eda 100644 --- a/include/ast/BlockNode.h +++ b/include/ast/BlockNode.h @@ -17,6 +17,8 @@ public: Vector<Node*> get_statements() const; Node* evaluate() const override; + Node* evaluate_same_scope() const; + String to_s() const override; private: diff --git a/include/parser/BuiltIn.h b/include/parser/BuiltIn.h index 130f73ebeb19f727762743a0c639987905fa5975..cd5dbd676f6f42cd84eb16f53c302c0d8d0fe3f6 100644 --- a/include/parser/BuiltIn.h +++ b/include/parser/BuiltIn.h @@ -16,6 +16,7 @@ class BuiltIn public: static Node* print(const CallNode& call, const Vector<ExpressionNode*>& args); static Node* read(const CallNode& call, const Vector<ExpressionNode*>& args); + static Node* fast_exit(const CallNode& call, const Vector<ExpressionNode*>& args); static HashMap<String, Node* (*)(const CallNode&, const Vector<ExpressionNode*>&)> functions; }; diff --git a/include/parser/Parser.h b/include/parser/Parser.h index d7e1462e62c0f09031a63d300f6c884fac1247aa..bb44e6ea3a8c878ba0837a852773f74881240c57 100644 --- a/include/parser/Parser.h +++ b/include/parser/Parser.h @@ -47,6 +47,12 @@ public: */ ~Parser(); + /** + * @brief Sets the tokens to parse + * @param tokens The tokens to parse + */ + void set_tokens(const Vector<Token>& tokens); + /** * @brief Parses the token stream and returns the root AST node * @param args The arguments passed to the program diff --git a/include/utils/Common.h b/include/utils/Common.h index 87fd2f0f64052bc0166c3b1a5f72cb9126394d11..2cfde8a494e978f3908fc09e932b40cf2233727a 100644 --- a/include/utils/Common.h +++ b/include/utils/Common.h @@ -20,6 +20,7 @@ using std::cerr; using std::cin; using std::cout; +using std::endl; using std::getline; namespace funk diff --git a/source/ast/BlockNode.cc b/source/ast/BlockNode.cc index 826cdfbace5962e014b8c7b7a57c12442b82e03f..c9892ec9e692b8279596d0747a8ba47d0ff9332b 100644 --- a/source/ast/BlockNode.cc +++ b/source/ast/BlockNode.cc @@ -30,6 +30,13 @@ Node* BlockNode::evaluate() const return result; } +Node* BlockNode::evaluate_same_scope() const +{ + Node* result{}; + for (Node* statement : statements) { result = statement->evaluate(); } + return result; +} + String BlockNode::to_s() const { String repr{}; diff --git a/source/main.cc b/source/main.cc index 87dc2f0b814a1837270269a169e1c468bb7786d7..54573944adcd1243b905969e9f8a7851f177e898 100644 --- a/source/main.cc +++ b/source/main.cc @@ -47,7 +47,7 @@ bool setup(ArgParser& parser, Config& config) // Print help message if (parser.has_option("--help")) { - cout << ArgParser::help("funk [options] <file>", options) << "\n"; + cout << ArgParser::help("funk [options] <file>", options) << endl; return false; } @@ -121,10 +121,62 @@ void process_file(const String& file, const Config& config, const Vector<String> catch (const FunkError& e) { LOG_ERROR("Error processing file " + file + ": " + e.what()); - cerr << "Error: " << e.trace() << '\n'; + cerr << "Error: " << e.trace() << endl; } } +/** + * @brief Funk REPL + * Reads and executes Funk code from the standard input + */ +void repl() +{ + cout << "Funk REPL. Press Ctrl+D to exit or type 'exit()'." << endl << endl; + + String input; + Vector<Token> tokens{}; + Parser parser{tokens, ""}; + Scope::instance().push(); + + while (true) + { + cout << ">>> "; + cout.flush(); + if (!getline(cin, input)) { break; } + if (input.empty()) { continue; } + + try + { + // Add a semicolon to the input if it doesn't end with one + if (input.back() != ';') { input += ";"; } + + Lexer lexer{input + "\n", ""}; + Vector<Token> tokens{lexer.tokenize()}; + + parser.set_tokens(tokens); + BlockNode* ast{static_cast<BlockNode*>(parser.parse())}; + + Node* result{ast->evaluate_same_scope()}; + if (result) { cout << result->to_s() << endl; } + } + catch (const FunkError& e) + { + cerr << "Error: " << e.trace() << endl; + } + catch (const std::exception& e) + { + cerr << "Error: " << e.what() << endl; + } + catch (...) + { + cerr << "Unknown error occurred" << endl; + } + } + + Scope::instance().pop(); + cout << "Bye!" << endl; +} + /** * @brief Main entry point for the Funk interpreter * Parses command line arguments and processes input files @@ -142,7 +194,7 @@ int main(int argc, char* argv[]) // Process each file if (parser.has_file()) { process_file(parser.get_file(), config, parser.get_args()); } - else { cout << "Funk REPL, not implemented yet\n"; } + else { repl(); } return 0; } diff --git a/source/parser/BuiltIn.cc b/source/parser/BuiltIn.cc index 005e8d7ccea807bb6b0f1078268a06db3da33b2d..66067137eb5f99c71c0d67920ee5d3b94000bb71 100644 --- a/source/parser/BuiltIn.cc +++ b/source/parser/BuiltIn.cc @@ -11,7 +11,7 @@ Node* BuiltIn::print(const CallNode& call, const Vector<ExpressionNode*>& args) if (!result) { throw RuntimeError(arg->get_location(), "Print argument did not evaluate to an expression"); } cout << result->get_value().cast<String>() << " "; } - cout << "\n"; + cout << endl; return new LiteralNode(call.get_location(), None{}); } @@ -23,7 +23,15 @@ Node* BuiltIn::read(const CallNode& call, const Vector<ExpressionNode*>& args) return new LiteralNode(call.get_location(), input); } +Node* BuiltIn::fast_exit(const CallNode& call [[maybe_unused]], const Vector<ExpressionNode*>& args) +{ + int status{0}; + if (!args.empty()) { status = dynamic_cast<LiteralNode*>(args[0]->evaluate())->get_value().cast<int>(); } + + exit(status); +} + HashMap<String, Node* (*)(const CallNode&, const Vector<ExpressionNode*>&)> BuiltIn::functions{ - {"print", print}, {"read", read}}; + {"print", print}, {"read", read}, {"exit", fast_exit}}; } // namespace funk diff --git a/source/parser/Parser.cc b/source/parser/Parser.cc index d35ab7090a8b6ede22da105723616b7e7c80c9b6..bcaca9fa61ea7c1c4fb27790aeceed4d927bc40a 100644 --- a/source/parser/Parser.cc +++ b/source/parser/Parser.cc @@ -24,6 +24,12 @@ Node* Parser::parse(const Vector<String>& args) return block; } +void Parser::set_tokens(const Vector<Token>& tokens) +{ + this->tokens = tokens; + this->index = 0; +} + Parser Parser::load(String filename) { Lexer lexer{read_file(filename), filename}; @@ -72,6 +78,9 @@ Node* Parser::parse_statement() { LOG_DEBUG("Parse statement"); + // Empty statement, just a semicolon + if (match(TokenType::SEMICOLON)) { return new LiteralNode(peek_prev().get_location(), NodeValue(None())); } + Node* control{parse_control()}; if (control) { return control; }