From 86904d513734134beffc29c6f4012d53a99f25c5 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Sun, 13 Apr 2025 15:38:34 +0000
Subject: [PATCH] some clean up, added function declaration
---
src/ScriptInterpreter.cpp | 222 +++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 156 insertions(+), 66 deletions(-)
diff --git a/src/ScriptInterpreter.cpp b/src/ScriptInterpreter.cpp
index 28d557c..92e1da6 100644
--- a/src/ScriptInterpreter.cpp
+++ b/src/ScriptInterpreter.cpp
@@ -9,17 +9,17 @@
#include "ScriptExceptionMacros.h"
#include "Value.hpp"
-void ScriptInterpreter::registerFunction(const std::string & name, std::shared_ptr<BaseFunction> fn) {
+void ScriptInterpreter::registerModule(const std::string & name, std::shared_ptr<BaseFunction> fn) {
functionObjects[name] = std::move(fn);
}
Value ScriptInterpreter::evaluateExpression(const Token & token) const {
if (token.type == TokenType::StringLiteral) {
- return Value::fromString(token.lexeme);
+ return Value::fromString(token);
}
if (token.type == TokenType::IntLiteral) {
try {
- return Value::fromInt(std::stoi(token.lexeme));
+ return Value::fromInt(token);
} catch (const std::invalid_argument & e) {
throw std::runtime_error("Invalid integer literal: " + token.lexeme);
} catch (const std::out_of_range & e) {
@@ -28,7 +28,7 @@
}
if (token.type == TokenType::DoubleLiteral) {
try {
- return Value::fromDouble(std::stod(token.lexeme));
+ return Value::fromDouble(token);
} catch (const std::invalid_argument & e) {
throw std::runtime_error("Invalid double literal: " + token.lexeme);
} catch (const std::out_of_range & e) {
@@ -36,24 +36,22 @@
}
}
if (token.type == TokenType::Variable) {
- if (variables.find(token.lexeme) != variables.end()) {
- return variables.at(token.lexeme);
- }
- THROW_UNDEFINED_VARIABLE_ERROR(token.lexeme, token);
- } else {
- THROW_UNEXPECTED_TOKEN_ERROR(token, "string, integer, double, or variable");
+ return this->getVariable(token);
}
+ THROW_UNEXPECTED_TOKEN_ERROR(token, "string, integer, double, or variable");
+
return Value();
}
-std::vector<Value> ScriptInterpreter::parseArguments(const std::vector<Token> & tokens,
- std::size_t & current_index) const {
+std::vector<Value> ScriptInterpreter::parseFunctionArguments(const std::vector<Token> & tokens,
+ std::size_t & index) const {
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::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));
@@ -77,10 +75,50 @@
THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], "')'");
}
current_index++; // Skip ')'
- current_index = current_index;
+ index = current_index;
return args;
}
+
+void ScriptInterpreter::handleBooleanDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
+ const auto varName = tokens[i].lexeme;
+ const auto varType = tokens[i].variableType;
+ i++; // Skip variable name
+ if (i < tokens.size() && tokens[i].type == TokenType::Equals) {
+ i++; // Skip '='
+
+ if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
+ const auto variable = this->getVariable(tokens[i]);
+
+ if (variable.type != Variables::Type::VT_BOOLEAN) {
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_BOOLEAN),
+ tokens[i].lexeme, variable.TypeToString(), tokens[i]);
+ }
+ this->setVariable(varName, variable);
+ i++; // Skip variable name
+ EXPECT_SEMICOLON(tokens, i, "after bool variable declaration");
+
+ } else if (i < tokens.size() && tokens[i].type == TokenType::Identifier) {
+ std::string lowered = tokens[i].lexeme;
+ std::transform(lowered.begin(), lowered.end(), lowered.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+
+ if (lowered == "true") {
+ this->setVariable(varName, Value::fromBoolean(tokens[i], true));
+ } else if (lowered == "false") {
+ this->setVariable(varName, Value::fromBoolean(tokens[i], false));
+ } else {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "true or false after '='");
+ }
+ i++; // Skip boolean literal
+ EXPECT_SEMICOLON(tokens, i, "after bool declaration");
+ } else {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "bool literal after '='");
+ }
+ } else {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after bool declaration");
+ }
+};
void ScriptInterpreter::handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
const auto varName = tokens[i].lexeme;
@@ -91,23 +129,21 @@
i++; // Skip '='
if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
- if (variables.find(tokens[i].lexeme) == variables.end()) {
- THROW_UNDEFINED_VARIABLE_ERROR(tokens[i].lexeme, tokens[i]);
- } else {
- if (variables[tokens[i].lexeme].type != Variables::Type::VT_STRING) {
- THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_STRING),
- tokens[i].lexeme, variables[tokens[i].lexeme].TypeToString(),
- tokens[i]);
- }
+ const auto variable = this->getVariable(tokens[i]);
- variables[varName] = variables[tokens[i].lexeme];
- i++; // Skip variable name
- expectSemicolon(tokens, i, "after string variable declaration");
+ if (variable.type != Variables::Type::VT_STRING) {
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_STRING),
+ tokens[i].lexeme, variable.TypeToString(), tokens[i]);
}
+ this->setVariable(varName, variable);
+ //variables[varName] = variables[tokens[i].lexeme];
+ i++; // Skip variable name
+ EXPECT_SEMICOLON(tokens, i, "after string variable declaration");
+
} else if (i < tokens.size() && tokens[i].type == TokenType::StringLiteral) {
- variables[varName] = Value::fromString(tokens[i].lexeme);
+ this->setVariable(varName, Value::fromString(tokens[i]));
i++; // Skip string literal
- expectSemicolon(tokens, i, "after string declaration");
+ EXPECT_SEMICOLON(tokens, i, "after string declaration");
} else {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "string literal after '='");
}
@@ -126,10 +162,12 @@
if (i < tokens.size()) {
if (type == TokenType::IntDeclaration && tokens[i].type == TokenType::IntLiteral) {
try {
- if (variables.find(varName) != variables.end()) {
- THROW_VARIABLE_REDEFINITION_ERROR(varName, tokens[i]);
+ const auto variable = this->getVariable(tokens[i]);
+ if (variable.type != Variables::Type::VT_INT) {
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_INT),
+ tokens[i].lexeme, variable.TypeToString(), tokens[i]);
}
- variables[varName] = Value::fromInt(std::stoi(tokens[i].lexeme));
+ this->setVariable(varName, Value::fromInt(tokens[i]));
i++; // Skip int literal
} catch (const std::invalid_argument & e) {
throw std::runtime_error("Invalid integer literal in declaration: " + tokens[i].lexeme);
@@ -138,10 +176,13 @@
}
} else if (type == TokenType::DoubleDeclaration && tokens[i].type == TokenType::DoubleLiteral) {
try {
- if (variables.find(varName) != variables.end()) {
- THROW_VARIABLE_REDEFINITION_ERROR(varName, tokens[i]);
+ const auto variable = this->getVariable(tokens[i]);
+ if (variable.type != Variables::Type::VT_DOUBLE) {
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName,
+ Variables::TypeToString(Variables::Type::VT_DOUBLE),
+ tokens[i].lexeme, variable.TypeToString(), tokens[i]);
}
- variables[varName] = Value::fromDouble(std::stod(tokens[i].lexeme));
+ this->setVariable(varName, Value::fromDouble(tokens[i]));
i++; // Skip double literal
} catch (const std::invalid_argument & e) {
throw std::runtime_error("Invalid double literal in declaration: " + tokens[i].lexeme);
@@ -153,7 +194,7 @@
THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, expectedType, "",
getVariableTypeFromTokenTypeAsString(tokens[i].type), tokens[i]);
}
- expectSemicolon(tokens, i, "after variable declaration");
+ EXPECT_SEMICOLON(tokens, i, "after variable declaration");
} else {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "literal after '='");
}
@@ -162,34 +203,87 @@
}
}
+void ScriptInterpreter::handleFunctionDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
+ const auto varName = tokens[i].lexeme;
+ const auto varType = tokens[i].variableType;
+
+ i++; // skip funct name
+
+ if (i < tokens.size() && tokens[i].type != TokenType::Equals) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after function declaration");
+ }
+ i++; // skip '='
+
+ if (this->functionParameters.find(varName) != this->functionParameters.end()) {
+ THROW_FUNCTION_REDEFINITION_ERROR(varName, tokens[i]);
+ }
+
+ if (i < tokens.size() && tokens[i].type != TokenType::LeftParenthesis) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "");
+ }
+ i++;
+ // parse arg definitions
+
+ const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i);
+ std::cout << "args: " << args.size() << std::endl;
+ for (const auto & arg : args) {
+ std::cout << "arg name: " << arg.GetToken().lexeme << " type: " << arg.TypeToString() << std::endl;
+ }
+ this->functionParameters[varName].assign(args.begin(), args.end());
+ const std::string body = ScriptInterpreterHelpers::getFunctionBody(tokens, i);
+ this->functionBodies[varName] = body;
+ // recheck the close curly brace
+ if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
+ }
+ i++;
+ //this->functionBodies[varName] = std::make_shared<ScriptInterpreter>();
+ //this->functionBodies[varName]->executeScript(body, this->filename, true);
+ //EXPECT_SEMICOLON(tokens, i, "after function declaration");
+ // there is no need semicolon to the end of the function declaration
+ std::cout << "function body: \n\"" << body << "\"" << std::endl;
+}
+
void ScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) {
+ auto index = i;
std::string funcName = tokens[i].lexeme;
- auto it = functionObjects.find(funcName);
+
+ index++; // skip funct name
+ if (index < tokens.size() && tokens[index].type != TokenType::LeftParenthesis) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[index - 1], "");
+ }
+
+ auto it = functionObjects.find(funcName);
if (it == functionObjects.end()) {
- throw std::runtime_error("Unknown function: " + funcName);
+ THROW_UNDEFINED_FUNCTION_ERROR(funcName, tokens[i]);
}
- it->second->validate(tokens, i);
- std::vector<Value> args = parseArguments(tokens, i);
+
+ // it->second->validate(tokens, i, this->variables);
+
+ std::vector<Value> args = parseFunctionArguments(tokens, index);
it->second->call(args);
- if (i < tokens.size() && tokens[i].type == TokenType::Semicolon) {
- i++; // Skip ';' after function call
- }
+ i = index;
+
+ 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) {
//THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "function call or variable assignment (not yet implemented)");
- const auto varName = tokens[i].lexeme;
- const auto varType = tokens[i].variableType;
+// const auto varName = tokens[i].lexeme;
+// const auto varType = tokens[i].variableType;
+ const auto& varToken = tokens[i];
i++; // Skip variable token to avoid infinite loop
if (i < tokens.size() && tokens[i].type == TokenType::Equals) {
i++; // Skip '='
if (i < tokens.size()) {
- if (variables.find(varName) == variables.end()) {
- THROW_UNDEFINED_VARIABLE_ERROR(varName, tokens[i]);
- }
- variables[varName] = evaluateExpression(tokens[i]);
+ const auto variable = this->getVariable(varToken);
+ this->setVariable(varToken.lexeme, evaluateExpression(tokens[i]));
i++; // Skip value
- expectSemicolon(tokens, i, "after variable assignment");
+ EXPECT_SEMICOLON(tokens, i, "after variable assignment");
} else {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "value after '='");
}
@@ -206,16 +300,8 @@
i++; // Skip semicolon token
}
-void ScriptInterpreter::expectSemicolon(const std::vector<Token> & tokens, std::size_t & i,
- const std::string & message) const {
- if (i >= tokens.size() || tokens[i].type != TokenType::Semicolon) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "; " + message);
- } else {
- i++; // Skip ';'
- }
-}
-
-void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool debug) {
+void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool ignore_tags) {
+ this->filename = filename;
Lexer lexer(source, filename);
auto tokens = lexer.tokenize();
@@ -240,17 +326,21 @@
continue;
}
- if (!insideScript) {
- // Csak kiíratás, ha nem vagyunk script tagben
- std::cout << token.lexeme;
+ if (insideScript == false && ignore_tags == false) {
+ //std::cout << token.lexeme;
i++;
continue;
}
- // A szokásos feldolgozás csak ha belül vagyunk
switch (token.type) {
case TokenType::StringDeclaration:
handleStringDeclaration(tokens, i);
+ break;
+ case TokenType::BooleanDeclaration:
+ handleBooleanDeclaration(tokens, i);
+ break;
+ case TokenType::FunctionDeclaration:
+ handleFunctionDeclaration(tokens, i);
break;
case TokenType::IntDeclaration:
case TokenType::DoubleDeclaration:
@@ -269,7 +359,7 @@
handleSemicolon(i);
break;
default:
- throw std::runtime_error("Unexpected token inside script: " + token.lexeme);
+ THROW_UNEXPECTED_TOKEN_ERROR(token, "");
}
}
}
--
Gitblit v1.9.3