From c34b2c57219aa496a202c2be1e12332b4eeea440 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Mon, 14 Apr 2025 15:43:20 +0000
Subject: [PATCH] add function parameter handling and contextes
---
src/ScriptInterpreter.cpp | 133 ++++++++++++++++++++++++++++----------------
1 files changed, 84 insertions(+), 49 deletions(-)
diff --git a/src/ScriptInterpreter.cpp b/src/ScriptInterpreter.cpp
index ee7cf20..1f353cf 100644
--- a/src/ScriptInterpreter.cpp
+++ b/src/ScriptInterpreter.cpp
@@ -54,11 +54,6 @@
std::vector<Value> args;
size_t current_index = index;
- // if (current_index >= tokens.size() || tokens[current_index].type != TokenType::Identifier) {
- // THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], tokenTypeNames.at(TokenType::Identifier));
- // }
- // current_index++; // Skip function name
-
if (current_index >= tokens.size() || tokens[current_index].type != TokenType::LeftParenthesis) {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], tokenTypeNames.at(TokenType::LeftParenthesis));
}
@@ -93,7 +88,7 @@
i++; // Skip '='
if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
- const auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
+ auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
if (variable.type != Variables::Type::VT_BOOLEAN) {
THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme,
@@ -111,9 +106,11 @@
[](unsigned char c) { return std::tolower(c); });
if (lowered == "true") {
- this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], true), this->contextPrefix, true);
+ auto value = Value::fromBoolean(tokens[i], true);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
} else if (lowered == "false") {
- this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], false), this->contextPrefix, true);
+ auto value = Value::fromBoolean(tokens[i], false);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
} else {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "true or false after '='");
}
@@ -122,9 +119,11 @@
} else if (i < tokens.size() && tokens[i].type == TokenType::IntLiteral) {
const auto test = std::stoi(tokens[i].lexeme);
if (test == 0) {
- this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], false), this->contextPrefix, true);
+ auto value = Value::fromBoolean(tokens[i], false);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
} else if (test > 0) {
- this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], true), this->contextPrefix, true);
+ auto value = Value::fromBoolean(tokens[i], false);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
} else {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "bool literal after '='");
}
@@ -154,12 +153,14 @@
Variables::TypeToString(Variables::Type::VT_STRING),
tokens[i].lexeme, variable.TypeToString(), tokens[i]);
}
- this->setVariable(varToken.lexeme, variable, this->contextPrefix, true);
+ auto value = variable;
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
i++; // Skip variable name
EXPECT_SEMICOLON(tokens, i, "after string variable declaration");
} else if (i < tokens.size() && tokens[i].type == TokenType::StringLiteral) {
- this->setVariable(varToken.lexeme, Value::fromString(tokens[i]), this->contextPrefix, true);
+ auto value = Value::fromString(tokens[i]);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
i++; // Skip string literal
EXPECT_SEMICOLON(tokens, i, "after string declaration");
} else {
@@ -179,7 +180,8 @@
if (i < tokens.size()) {
if (type == TokenType::IntDeclaration && tokens[i].type == TokenType::IntLiteral) {
try {
- this->setVariable(varToken.lexeme, Value::fromInt(tokens[i]), this->contextPrefix, true);
+ auto value = Value::fromInt(tokens[i]);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
i++; // Skip int literal
} catch (const std::invalid_argument & e) {
throw std::runtime_error("Invalid integer literal in declaration: " + tokens[i].lexeme);
@@ -188,7 +190,8 @@
}
} else if (type == TokenType::DoubleDeclaration && tokens[i].type == TokenType::DoubleLiteral) {
try {
- this->setVariable(varToken.lexeme, Value::fromDouble(tokens[i]), this->contextPrefix, true);
+ auto value = Value::fromDouble(tokens[i]);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
i++; // Skip double literal
} catch (const std::invalid_argument & e) {
throw std::runtime_error("Invalid double literal in declaration: " + tokens[i].lexeme);
@@ -210,7 +213,7 @@
}
void ScriptInterpreter::handleFunctionDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
- const auto varName = tokens[i].lexeme;
+ const auto & funcToken = tokens[i];
i++; // skip funct name
@@ -219,8 +222,8 @@
}
i++; // skip '='
- if (this->functionParameters.find(varName) != this->functionParameters.end()) {
- THROW_FUNCTION_REDEFINITION_ERROR(varName, tokens[i]);
+ if (this->functionBodies.find(funcToken.lexeme) != this->functionBodies.end()) {
+ THROW_FUNCTION_REDEFINITION_ERROR(funcToken.lexeme, tokens[i]);
}
if (i < tokens.size() && tokens[i].type != TokenType::LeftParenthesis) {
@@ -228,54 +231,67 @@
}
i++;
// parse arg definitions
-
- const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i, __FILE__, __LINE__);
- std::cout << "args: " << args.size() << '\n';
+ const std::string context_name = this->getContextName(funcToken.lexeme);
+ const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i, __FILE__, __LINE__);
for (const auto & arg : args) {
- std::cout << "arg name: " << arg.GetToken().lexeme << " type: " << arg.TypeToString() << '\n';
+ auto value = arg;
+ this->setVariable(arg.GetToken().lexeme, value, context_name, true);
}
- this->functionParameters[varName].assign(args.begin(), args.end());
+
size_t start;
size_t end;
ScriptInterpreterHelpers::getFunctionBody(tokens, i, start, end);
- std::cout << "Body start: " << start << " end: " << end << '\n';
+
const std::string function_body = ScriptInterpreterHelpers::extractSubstring(this->source, start, end);
- this->functionBodies[varName] = function_body;
+ if (function_body.empty()) {
+ std::cout << this->source << '\n';
+ THROW_FUNCTION_BODY_EMPTY(funcToken.lexeme, tokens[i - 1]);
+ }
+ this->functionBodies[funcToken.lexeme] = function_body;
// recheck the close curly brace
if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
}
i++;
-#if DEBUG_BUILD == 1
- std::cout << "function body: \n\"" << function_body << "\"" << '\n';
-#endif
}
void ScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) {
- auto index = i;
- std::string funcName = tokens[i].lexeme;
+ const auto & functiontoken = tokens[i];
- index++; // skip funct name
- if (index < tokens.size() && tokens[index].type != TokenType::LeftParenthesis) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index - 1], "");
+ i++; // skip funct name
+ if (i < tokens.size() && tokens[i].type != TokenType::LeftParenthesis) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "");
}
- auto it = functionObjects.find(funcName);
- if (it == functionObjects.end()) {
- THROW_UNDEFINED_FUNCTION_ERROR(funcName, tokens[i]);
+ std::vector<Value> args = parseFunctionArguments(tokens, i);
+
+ auto it1 = functionObjects.find(functiontoken.lexeme);
+ if (it1 != functionObjects.end()) {
+ it1->second->call(args);
+ } else {
+ auto it2 = functionBodies.find(functiontoken.lexeme);
+ if (it2 == functionBodies.end()) {
+ THROW_UNDEFINED_FUNCTION_ERROR(functiontoken.lexeme, tokens[i]);
+ }
+
+ if (args.size() > 0) {
+ auto varList = this->getcontextVariables(this->getContextName(functiontoken.lexeme));
+ if (varList.size() != args.size()) {
+ THROW_FUNCTION_ARG_COUNT_MISMATCH_ERROR(functiontoken.lexeme, varList.size(), args.size(), tokens[i]);
+ }
+ size_t counter = 0;
+ for (auto & var : varList) {
+ this->setVariable(var.first, args[counter], var.second.context, false, true);
+ counter++;
+ }
+ }
+
+ this->executeScript(it2->second, this->filename, functiontoken.lexeme, true);
}
- // it->second->validate(tokens, i, this->variables);
-
- std::vector<Value> args = parseFunctionArguments(tokens, index);
- it->second->call(args);
- i = index;
+ //i++;
EXPECT_SEMICOLON(tokens, i, "after function call");
-
- // if (i < tokens.size() && tokens[i].type == TokenType::Semicolon) {
- // i++; // Skip ';' after function call
- // }
}
void ScriptInterpreter::handleVariableReference(const std::vector<Token> & tokens, std::size_t & i) {
@@ -285,7 +301,8 @@
i++; // Skip '='
if (i < tokens.size()) {
const auto variable = this->getVariable(varToken, this->contextPrefix, __FILE__, __LINE__);
- this->setVariable(varToken.lexeme, evaluateExpression(tokens[i]), this->contextPrefix, false);
+ auto value = evaluateExpression(tokens[i]);
+ this->setVariable(varToken.lexeme, value, this->contextPrefix, false);
i++; // Skip value
EXPECT_SEMICOLON(tokens, i, "after variable assignment");
} else {
@@ -298,9 +315,19 @@
void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename,
const std::string & _namespace, bool ignore_tags) {
- this->filename = filename;
- this->source = source;
- this->contextPrefix = filename + _namespace;
+ std::string oldContext;
+ std::string oldSource;
+
+ this->filename = filename;
+ if (!this->source.empty()) {
+ oldSource = this->source;
+ }
+ this->source = source;
+
+ if (!this->contextPrefix.empty()) {
+ oldContext = this->contextPrefix;
+ }
+ this->contextPrefix = this->getContextName(_namespace);
Lexer lexer(source, filename);
auto tokens = lexer.tokenize();
@@ -345,7 +372,7 @@
case TokenType::DoubleDeclaration:
handleNumberDeclaration(tokens, i, token.type);
break;
- case TokenType::Identifier:
+ case TokenType::FunctionCall:
handleFunctionCall(tokens, i);
break;
case TokenType::Variable:
@@ -361,4 +388,12 @@
THROW_UNEXPECTED_TOKEN_ERROR(token, "");
}
}
+ if (!oldContext.empty()) {
+ this->contextPrefix = oldContext;
+ oldContext.clear();
+ }
+ if (!oldSource.empty()) {
+ this->source = oldSource;
+ oldSource.clear();
+ }
}
--
Gitblit v1.9.3