From acb1c33ab258dc14dcc51b405b44cc5923b34324 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Fri, 18 Apr 2025 08:52:16 +0000
Subject: [PATCH] add function call
---
src/Parser/Parser.cpp | 9 +
/dev/null | 144 ------------------------
src/Interpreter/CallStatementNode.hpp | 90 +++++++++++++++
src/Symbols/SymbolFactory.hpp | 5
src/Interpreter/OperationsFactory.hpp | 18 ++
src/Lexer/Operators.hpp | 12 +
test_scripts/test2.vs | 8
src/Interpreter/Interpreter.hpp | 33 +++--
8 files changed, 150 insertions(+), 169 deletions(-)
diff --git "a/src/\041NameSpaceManager.hpp" "b/src/\041NameSpaceManager.hpp"
deleted file mode 100644
index fa249e9..0000000
--- "a/src/\041NameSpaceManager.hpp"
+++ /dev/null
@@ -1,144 +0,0 @@
-#ifndef NAMESPACE_MANAGER_H
-#define NAMESPACE_MANAGER_H
-#include <functional>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-class Namespace {
- public:
- Namespace(const std::string & name = "", Namespace * parent = nullptr) : name_(name), parent_(parent) {}
-
- /**
- * @brief Add a child namespace to the current namespace.
- * @param name Name of the child namespace.
- */
- Namespace * addChild(const std::string & name) {
- auto it = children_.find(name);
- if (it == children_.end()) {
- children_[name] = std::make_unique<Namespace>(name, this);
- }
- return children_[name].get();
- }
-
- /**
- * @brief Get the child namespace by name.
- * @param name Name of the child namespace.
- * @return The child namespace
- */
- Namespace * getChild(const std::string & name) const {
- auto it = children_.find(name);
- return it != children_.end() ? it->second.get() : nullptr;
- }
-
- /**
- * @brief Get the child namespace by name.
- * @param name Name of the child namespace.
- * @return The child namespace
- */
- Namespace * getOrCreate(const std::string & fullName) {
- auto parts = split(fullName, '.');
- Namespace * current = this;
- for (const auto & part : parts) {
- current = current->addChild(part);
- }
- return current;
- }
-
- /**
- * @brief Get the parent namespace.
- * @return The parent namespace.
- */
- Namespace * getParent() const { return parent_; }
-
- std::string toString() const {
- if (!parent_ || name_.empty()) {
- return name_;
- }
- return parent_->toString() + "." + name_;
- }
-
- void traverse(const std::function<void(const Namespace &)>& visitor) const {
- visitor(*this);
- for (const auto & [_, child] : children_) {
- child->traverse(visitor);
- }
- }
-
- const std::string & getName() const { return name_; }
-
- private:
- std::string name_;
- Namespace * parent_;
- std::map<std::string, std::unique_ptr<Namespace>> children_;
-
- static std::vector<std::string> split(const std::string & str, char delimiter) {
- std::stringstream ss(str);
- std::string part;
- std::vector<std::string> result;
- while (std::getline(ss, part, delimiter)) {
- result.push_back(part);
- }
- return result;
- }
-};
-
-class NamespaceManager {
- public:
- static NamespaceManager & instance() {
- static NamespaceManager instance_;
- return instance_;
- }
-
- /**
- * @brief Constructor for NamespaceManager.
- * @param name Name of the namespace.
- * @param parent Parent namespace.
- */
- NamespaceManager() : root_(""), currentNamespace_(&root_) {}
-
- /**
- * @brief Get or create a namespace by full name.
- * @param fullName Full name of the namespace.
- * @return The namespace object.
- */
- Namespace * getOrCreate(const std::string & fullName) { return root_.getOrCreate(fullName); }
-
- /**
- * @brief Set the current namespace.
- * @param fullName Full name of the namespace.
- */
- void setCurrent(const std::string & fullName) {
- Namespace * ns = root_.getOrCreate(fullName);
- if (ns) {
- currentNamespace_ = ns;
- } else {
- currentNamespace_ = &root_; // fallback
- }
- }
-
- /**
- * @brief Get the current namespace.
- * @return The current namespace object.
- */
- Namespace * getCurrent() const { return currentNamespace_; }
-
- /**
- * @brief Reset the current namespace.
- */
- void resetCurrent() { currentNamespace_ = &root_; }
-
- /**
- * @brief Traverse the namespace tree.
- * @param visitor A function to visit each namespace.
- */
- void traverse(const std::function<void(const Namespace &)>& visitor) const { root_.traverse(visitor); }
-
- private:
- Namespace root_;
- Namespace * currentNamespace_;
-};
-
-#endif // NAMESPACE_MANAGER_H
diff --git a/src/Interpreter/CallStatementNode.hpp b/src/Interpreter/CallStatementNode.hpp
new file mode 100644
index 0000000..ccd85a1
--- /dev/null
+++ b/src/Interpreter/CallStatementNode.hpp
@@ -0,0 +1,90 @@
+#ifndef INTERPRETER_CALL_STATEMENT_NODE_HPP
+#define INTERPRETER_CALL_STATEMENT_NODE_HPP
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "ExpressionNode.hpp"
+#include "Interpreter/Interpreter.hpp"
+#include "Interpreter/OperationContainer.hpp"
+#include "StatementNode.hpp"
+#include "Symbols/FunctionSymbol.hpp"
+#include "Symbols/SymbolContainer.hpp"
+#include "Symbols/SymbolFactory.hpp"
+#include "Symbols/Value.hpp"
+
+namespace Interpreter {
+
+/**
+ * @brief Statement node representing a function call with argument expressions.
+ */
+class CallStatementNode : public StatementNode {
+ std::string functionName_;
+ std::vector<std::unique_ptr<ExpressionNode>> args_;
+
+ public:
+ CallStatementNode(const std::string & functionName, std::vector<std::unique_ptr<ExpressionNode>> args,
+ const std::string & file_name, int file_line, size_t column) :
+ StatementNode(file_name, file_line, column),
+ functionName_(functionName),
+ args_(std::move(args)) {}
+
+ void interpret(Interpreter & interpreter) const override {
+ using namespace Symbols;
+ // Evaluate argument expressions
+ std::vector<Value> argValues;
+ argValues.reserve(args_.size());
+ for (const auto & expr : args_) {
+ argValues.push_back(expr->evaluate(interpreter));
+ }
+
+ // Lookup function symbol in functions namespace
+ SymbolContainer * sc = SymbolContainer::instance();
+ const std::string currentNs = sc->currentScopeName();
+ const std::string fnSymNs = currentNs + ".functions";
+ auto sym = sc->get(fnSymNs, functionName_);
+ if (!sym || sym->getKind() != Kind::Function) {
+ throw std::runtime_error("Function not found: " + functionName_);
+ }
+ auto funcSym = std::static_pointer_cast<FunctionSymbol>(sym);
+
+ // Check parameter count
+ const auto & params = funcSym->parameters();
+ if (params.size() != argValues.size()) {
+ throw std::runtime_error("Function '" + functionName_ + "' expects " + std::to_string(params.size()) +
+ " args, got " + std::to_string(argValues.size()));
+ }
+
+ // Enter function scope to bind parameters and execute body
+ const std::string fnOpNs = currentNs + "." + functionName_;
+ sc->enter(fnOpNs);
+ // Bind parameters as local variables
+ for (size_t i = 0; i < params.size(); ++i) {
+ const auto & p = params[i];
+ const Value & v = argValues[i];
+
+ auto varSym = SymbolFactory::createVariable(p.name, v, fnOpNs);
+ sc->add(varSym);
+ }
+ // Execute function body operations
+ auto ops = Operations::Container::instance()->getAll(fnOpNs);
+ auto it = ops.begin();
+ for (; it != ops.end(); ++it) {
+ interpreter.runOperation(*(*it));
+ }
+ // Exit function scope
+ sc->enterPreviousScope();
+ }
+
+ std::string toString() const override {
+ return "CallStatementNode{ functionName='" + functionName_ + "', " +
+ "args=" + std::to_string(args_.size()) + " " + "filename='" + filename_ + "', " +
+ "line=" + std::to_string(line_) + ", " + "column=" + std::to_string(column_) + "}";
+ };
+};
+
+} // namespace Interpreter
+
+// namespace Interpreter
+#endif // INTERPRETER_CALL_STATEMENT_NODE_HPP
diff --git a/src/Interpreter/Interpreter.hpp b/src/Interpreter/Interpreter.hpp
index b8e2ce8..fe1dfb7 100644
--- a/src/Interpreter/Interpreter.hpp
+++ b/src/Interpreter/Interpreter.hpp
@@ -12,6 +12,22 @@
class Interpreter {
private:
+
+
+ public:
+ Interpreter() {}
+
+ /**
+ * @brief Execute all operations in the current namespace (e.g., file-level or function-level).
+ */
+ void run() {
+ // Determine namespace to execute
+ const std::string ns = Symbols::SymbolContainer::instance()->currentScopeName();
+ for (const auto & operation : Operations::Container::instance()->getAll(ns)) {
+ runOperation(*operation);
+ }
+ }
+
void runOperation(const Operations::Operation & op) {
std::cout << "Operation: " << op.toString() << "\n";
@@ -39,13 +55,11 @@
break;
}
- case Operations::Type::FunctionCall: {
- // Check that the called function is defined in the symbol table
- if (!Symbols::SymbolContainer::instance()->exists(op.targetName)) {
- throw std::runtime_error("Function not declared: " + op.targetName);
+ case Operations::Type::FunctionCall:
+ if (op.statement) {
+ op.statement->interpret(*this);
}
break;
- }
case Operations::Type::Return:
case Operations::Type::Loop:
case Operations::Type::Break:
@@ -58,15 +72,6 @@
break;
default:
throw std::runtime_error("Not implemented operation type");
- }
- }
-
- public:
- Interpreter() {}
-
- void run() {
- for (const auto & operation : Operations::Container::instance()->getAll()) {
- runOperation(*operation);
}
}
diff --git a/src/Interpreter/OperationsFactory.hpp b/src/Interpreter/OperationsFactory.hpp
index 2dc39af..2034a72 100644
--- a/src/Interpreter/OperationsFactory.hpp
+++ b/src/Interpreter/OperationsFactory.hpp
@@ -8,6 +8,7 @@
#include "Interpreter/DeclareVariableStatementNode.hpp"
#include "Interpreter/ExpressionBuilder.hpp"
#include "Interpreter/LiteralExpressionNode.hpp"
+#include "Interpreter/CallStatementNode.hpp"
#include "Interpreter/Operation.hpp"
#include "Interpreter/OperationContainer.hpp"
#include "Parser/ParsedExpression.hpp"
@@ -54,22 +55,31 @@
}
/**
- * @brief Record a function call operation for later detection.
+ * @brief Record a function call operation with argument expressions.
* @param functionName Name of the function being called.
- * @param ns Current namespace scope.
+ * @param parsedArgs Vector of parsed argument expressions.
+ * @param ns Current namespace scope for operations.
* @param fileName Source filename.
* @param line Line number of call.
* @param column Column number of call.
*/
static void callFunction(const std::string & functionName,
+ std::vector<Parser::ParsedExpressionPtr> &&parsedArgs,
const std::string & ns,
const std::string & fileName,
int line,
size_t column) {
- // No associated StatementNode; this is for detection only
+ // Build argument ExpressionNode list
+ std::vector<std::unique_ptr<ExpressionNode>> exprs;
+ exprs.reserve(parsedArgs.size());
+ for (auto &pexpr : parsedArgs) {
+ exprs.push_back(buildExpressionFromParsed(pexpr));
+ }
+ // Create call statement node
+ auto stmt = std::make_unique<CallStatementNode>(functionName, std::move(exprs), fileName, line, column);
Operations::Container::instance()->add(
ns,
- Operations::Operation{Operations::Type::FunctionCall, functionName, nullptr});
+ Operations::Operation{Operations::Type::FunctionCall, functionName, std::move(stmt)});
}
};
diff --git a/src/Lexer/Operators.hpp b/src/Lexer/Operators.hpp
index 690efe5..6725b99 100644
--- a/src/Lexer/Operators.hpp
+++ b/src/Lexer/Operators.hpp
@@ -59,8 +59,16 @@
std::vector<Parser::ParsedExpressionPtr> & output_queue) {
if (token.type == Tokens::Type::NUMBER || token.type == Tokens::Type::STRING_LITERAL ||
token.type == Tokens::Type::KEYWORD) {
- output_queue.push_back(
- Parser::ParsedExpression::makeLiteral(Symbols::Value::fromString(token.value, expected_var_type)));
+ // Parse literal: use expected type if provided, otherwise auto-detect
+ if (expected_var_type == Symbols::Variables::Type::NULL_TYPE) {
+ output_queue.push_back(
+ Parser::ParsedExpression::makeLiteral(
+ Symbols::Value::fromString(token.value, /*autoDetectType*/ true)));
+ } else {
+ output_queue.push_back(
+ Parser::ParsedExpression::makeLiteral(
+ Symbols::Value::fromString(token.value, expected_var_type)));
+ }
return true;
}
if (token.type == Tokens::Type::VARIABLE_IDENTIFIER) {
diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp
index b59b193..3e2bde8 100644
--- a/src/Parser/Parser.cpp
+++ b/src/Parser/Parser.cpp
@@ -133,8 +133,13 @@
expect(Lexer::Tokens::Type::PUNCTUATION, ")");
expect(Lexer::Tokens::Type::PUNCTUATION, ";");
// Record the function call operation
- Interpreter::OperationsFactory::callFunction(func_name, Symbols::SymbolContainer::instance()->currentScopeName(),
- this->current_filename_, id_token.line_number, id_token.column_number);
+ Interpreter::OperationsFactory::callFunction(
+ func_name,
+ std::move(args),
+ Symbols::SymbolContainer::instance()->currentScopeName(),
+ this->current_filename_,
+ id_token.line_number,
+ id_token.column_number);
}
Symbols::Value Parser::parseNumericLiteral(const std::string & value, bool is_negative, Symbols::Variables::Type type) {
diff --git a/src/Symbols/SymbolFactory.hpp b/src/Symbols/SymbolFactory.hpp
index 5b7a168..dd9007f 100644
--- a/src/Symbols/SymbolFactory.hpp
+++ b/src/Symbols/SymbolFactory.hpp
@@ -14,6 +14,11 @@
class SymbolFactory {
public:
static std::shared_ptr<Symbol> createVariable(const std::string & name, const Symbols::Value & value,
+ const std::string & context) {
+ return std::make_shared<VariableSymbol>(name, value, context, value.getType());
+ }
+
+ static std::shared_ptr<Symbol> createVariable(const std::string & name, const Symbols::Value & value,
const std::string & context, Variables::Type type) {
return std::make_shared<VariableSymbol>(name, value, context, type);
}
diff --git a/test_scripts/test2.vs b/test_scripts/test2.vs
index ab60178..bb9d822 100644
--- a/test_scripts/test2.vs
+++ b/test_scripts/test2.vs
@@ -4,7 +4,9 @@
string $variable2 = $variable;
-print("$double: ", $double, "\n");
-print("$variable2: ", $variable2, "\n");
-print("$variable: ", $variable, "\n");
+function test = (int $i) {
+ int $result = $i + 1;
+}
+
+test(5);
\ No newline at end of file
--
Gitblit v1.9.3