From 832583b72e69bb4b5682ca7104d4501b6f6bad6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Alo=C3=ADsio?= Date: Sat, 12 Sep 2026 23:02:11 -0300 Subject: [PATCH 1/2] Add function support --- CMakeLists.txt | 3 ++ README.md | 23 +++++++++- src/interpreter/Interpreter.cpp | 65 ++++++++++++++++++++++++++++ src/interpreter/Interpreter.h | 1 + src/interpreter/Parser.cpp | 77 +++++++++++++++++++++++++++++++++ src/interpreter/Parser.h | 6 +++ 6 files changed, 173 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c4d31..6216469 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,9 @@ add_executable(python_interpreter src/main.cpp src/interpreter/Node/IfNode.h src/interpreter/Node/ForNode.h src/interpreter/Node/RangeNode.h + src/interpreter/Node/FunctionDefNode.h + src/interpreter/Node/ReturnNode.h + src/interpreter/Node/CallNode.h src/interpreter/Parser.cpp src/interpreter/Parser.h src/interpreter/Value.h diff --git a/README.md b/README.md index 6c020c7..05f4f1a 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,33 @@ cd build python_interpreter.exe path/to/script.py ``` +Supported language features +---------------------------- +- Variables, numbers, strings and arithmetic/comparison expressions (`+ - * / > < ==`, unary `-`) +- `print(...)` with multiple comma-separated arguments +- `if` / `else` +- `for x in range(...)`, with the same signatures as Python: + - `range(end)` + - `range(start, end)` + - `range(start, end, step)` (including negative steps, e.g. counting down) +- Functions with `def`, parameters and `return` (including recursion). Functions have their own local scope: assignments inside a function do not leak to the caller. + Example script (example.py) --------------------------- -``` +```python # example.py x = 0 for i in range(5): print(i) print("End of loop") + +def factorial(n): + if n < 2: + return 1 + return n * factorial(n - 1) + +print(factorial(5)) ``` Expected output (implementation-dependent): @@ -60,6 +78,7 @@ Expected output (implementation-dependent): 3 4 End of loop +120 ``` Code structure @@ -67,5 +86,5 @@ Code structure - `interpreter/Lexer.*` - tokenization - `interpreter/Parser.*` - AST construction - `interpreter/Interpreter.*` - AST execution/evaluation -- `interpreter/Node/` - AST node definitions (AssignNode, ForNode, PrintNode, etc.) +- `interpreter/Node/` - AST node definitions (AssignNode, ForNode, PrintNode, FunctionDefNode, ReturnNode, CallNode, etc.) - `interpreter/File.*` - file reading utilities diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index 6588114..d5dd045 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -29,9 +29,22 @@ along with this program; if not, see #include "Node/IfNode.h" #include "Node/ForNode.h" #include "Node/RangeNode.h" +#include "Node/FunctionDefNode.h" +#include "Node/ReturnNode.h" +#include "Node/CallNode.h" #include +namespace { + class ReturnSignal : public std::exception { + public: + Value value; + + explicit ReturnSignal(Value v) : value(std::move(v)) { + } + }; +} + Value Interpreter::executeNode(std::shared_ptr node) { if (auto num = dynamic_pointer_cast(node)) { return Value(num->value); @@ -134,6 +147,58 @@ Value Interpreter::executeNode(std::shared_ptr node) { return Value(0.0); } + if (auto funcDef = dynamic_pointer_cast(node)) { + functions[funcDef->name] = funcDef; + return Value(0.0); + } + + if (auto ret = dynamic_pointer_cast(node)) { + Value val = ret->value ? executeNode(ret->value) : Value(0.0); + throw ReturnSignal(val); + } + + if (auto call = dynamic_pointer_cast(node)) { + auto it = functions.find(call->name); + if (it == functions.end()) { + throw std::runtime_error("Function '" + call->name + "' not defined"); + } + auto funcDef = dynamic_pointer_cast(it->second); + + if (call->arguments.size() != funcDef->params.size()) { + throw std::runtime_error("Function '" + call->name + "' expects " + + std::to_string(funcDef->params.size()) + " argument(s), got " + + std::to_string(call->arguments.size())); + } + + std::vector argValues; + for (const auto &arg: call->arguments) { + argValues.push_back(executeNode(arg)); + } + + std::map callerVariables = variables; + std::map localVariables = variables; + for (size_t i = 0; i < funcDef->params.size(); i++) { + localVariables[funcDef->params[i]] = argValues[i]; + } + variables = localVariables; + + Value result(0.0); + try { + for (const auto &stmt: funcDef->body) + executeNode(stmt); + } catch (ReturnSignal &signal) { + result = signal.value; + variables = callerVariables; + return result; + } catch (...) { + variables = callerVariables; + throw; + } + + variables = callerVariables; + return result; + } + throw std::runtime_error("Unknown node type"); } diff --git a/src/interpreter/Interpreter.h b/src/interpreter/Interpreter.h index 3aa9b0f..b4885b0 100644 --- a/src/interpreter/Interpreter.h +++ b/src/interpreter/Interpreter.h @@ -29,6 +29,7 @@ along with this program; if not, see class Interpreter { private: std::map variables; + std::map > functions; std::vector output; Value executeNode(std::shared_ptr node); diff --git a/src/interpreter/Parser.cpp b/src/interpreter/Parser.cpp index b25dcf1..5aa6607 100644 --- a/src/interpreter/Parser.cpp +++ b/src/interpreter/Parser.cpp @@ -22,11 +22,14 @@ along with this program; if not, see #include "Parser.h" #include "Node/AssignNode.h" #include "Node/BinaryOpNode.h" +#include "Node/CallNode.h" #include "Node/ForNode.h" +#include "Node/FunctionDefNode.h" #include "Node/IfNode.h" #include "Node/NumberNode.h" #include "Node/PrintNode.h" #include "Node/RangeNode.h" +#include "Node/ReturnNode.h" #include "Node/StringNode.h" #include "Node/VariableNode.h" @@ -75,6 +78,11 @@ std::shared_ptr Parser::parsePrimary() { if (currentToken().type == TokenType::IDENTIFIER) { std::string name = currentToken().value; advance(); + + if (currentToken().type == TokenType::LPAREN) { + return parseCall(name); + } + return std::make_shared(name); } @@ -237,11 +245,76 @@ std::shared_ptr Parser::parseFor() { return make_shared(varName, iterable, body); } +std::shared_ptr Parser::parseCall(const std::string &name) { + expect(TokenType::LPAREN); + + std::vector > args; + if (currentToken().type != TokenType::RPAREN) { + args.push_back(parseExpression()); + while (currentToken().type == TokenType::COMMA) { + advance(); + args.push_back(parseExpression()); + } + } + + expect(TokenType::RPAREN); + return make_shared(name, args); +} + +std::shared_ptr Parser::parseDef() { + expect(TokenType::KEYWORD); + std::string name = currentToken().value; + expect(TokenType::IDENTIFIER); + expect(TokenType::LPAREN); + + std::vector params; + if (currentToken().type != TokenType::RPAREN) { + params.push_back(currentToken().value); + expect(TokenType::IDENTIFIER); + while (currentToken().type == TokenType::COMMA) { + advance(); + params.push_back(currentToken().value); + expect(TokenType::IDENTIFIER); + } + } + + expect(TokenType::RPAREN); + expect(TokenType::COLON); + expect(TokenType::NEWLINE); + expect(TokenType::INDENT); + + std::vector > body; + while (currentToken().type != TokenType::DEDENT && currentToken().type != TokenType::END_OF_FILE) { + body.push_back(parseStatement()); + skipNewlines(); + } + + if (currentToken().type == TokenType::DEDENT) + advance(); + + return make_shared(name, params, body); +} + +std::shared_ptr Parser::parseReturn() { + expect(TokenType::KEYWORD); + + std::shared_ptr value = nullptr; + if (currentToken().type != TokenType::NEWLINE && + currentToken().type != TokenType::DEDENT && + currentToken().type != TokenType::END_OF_FILE) { + value = parseExpression(); + } + + return make_shared(value); +} + std::shared_ptr Parser::parseStatement() { if (currentToken().type == TokenType::KEYWORD) { if (currentToken().value == "print") return parsePrint(); if (currentToken().value == "if") return parseIf(); if (currentToken().value == "for") return parseFor(); + if (currentToken().value == "def") return parseDef(); + if (currentToken().value == "return") return parseReturn(); } if (currentToken().type == TokenType::IDENTIFIER) { @@ -253,6 +326,10 @@ std::shared_ptr Parser::parseStatement() { auto value = parseExpression(); return make_shared(varName, value); } + + if (currentToken().type == TokenType::LPAREN) { + return parseCall(varName); + } } throw std::runtime_error("Invalid statement on line " + std::to_string(currentToken().line)); diff --git a/src/interpreter/Parser.h b/src/interpreter/Parser.h index 39345e1..b96e964 100644 --- a/src/interpreter/Parser.h +++ b/src/interpreter/Parser.h @@ -54,6 +54,12 @@ class Parser { std::shared_ptr parseFor(); + std::shared_ptr parseDef(); + + std::shared_ptr parseReturn(); + + std::shared_ptr parseCall(const std::string &name); + std::shared_ptr parseStatement(); public: From 85f466a3e2bceed36363bf420e99470d1a164be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Alo=C3=ADsio?= Date: Sat, 12 Sep 2026 23:06:14 -0300 Subject: [PATCH 2/2] Add function support --- src/interpreter/Node/CallNode.h | 35 ++++++++++++++++++++++++ src/interpreter/Node/FunctionDefNode.h | 37 ++++++++++++++++++++++++++ src/interpreter/Node/ReturnNode.h | 33 +++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/interpreter/Node/CallNode.h create mode 100644 src/interpreter/Node/FunctionDefNode.h create mode 100644 src/interpreter/Node/ReturnNode.h diff --git a/src/interpreter/Node/CallNode.h b/src/interpreter/Node/CallNode.h new file mode 100644 index 0000000..af98432 --- /dev/null +++ b/src/interpreter/Node/CallNode.h @@ -0,0 +1,35 @@ +/* +Copyright (C) 2025 M3t4l + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see +. +*/ + + +#ifndef CALLNODE_H +#define CALLNODE_H + + +class CallNode : public ASTNode { +public: + std::string name; + std::vector > arguments; + + CallNode(const std::string &n, const std::vector > &args) + : name(n), arguments(args) { + } +}; + + +#endif //CALLNODE_H diff --git a/src/interpreter/Node/FunctionDefNode.h b/src/interpreter/Node/FunctionDefNode.h new file mode 100644 index 0000000..a991b21 --- /dev/null +++ b/src/interpreter/Node/FunctionDefNode.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2025 M3t4l + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see +. +*/ + + +#ifndef FUNCTIONDEFNODE_H +#define FUNCTIONDEFNODE_H + + +class FunctionDefNode : public ASTNode { +public: + std::string name; + std::vector params; + std::vector > body; + + FunctionDefNode(const std::string &n, const std::vector &p, + const std::vector > &b) + : name(n), params(p), body(b) { + } +}; + + +#endif //FUNCTIONDEFNODE_H diff --git a/src/interpreter/Node/ReturnNode.h b/src/interpreter/Node/ReturnNode.h new file mode 100644 index 0000000..b14201a --- /dev/null +++ b/src/interpreter/Node/ReturnNode.h @@ -0,0 +1,33 @@ +/* +Copyright (C) 2025 M3t4l + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see +. +*/ + + +#ifndef RETURNNODE_H +#define RETURNNODE_H + + +class ReturnNode : public ASTNode { +public: + std::shared_ptr value; + + ReturnNode(std::shared_ptr v) : value(v) { + } +}; + + +#endif //RETURNNODE_H