From cb3065c34756a70cb6006fc25777ce3e720ff1a8 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Sun, 13 Apr 2025 18:16:19 +0000
Subject: [PATCH] implement variable contexts, add function body store and parsing
---
src/ScriptInterpreter.cpp | 106 ++++++++++++++++++++++++++++------------------------
1 files changed, 57 insertions(+), 49 deletions(-)
diff --git a/src/ScriptInterpreter.cpp b/src/ScriptInterpreter.cpp
index 92e1da6..3735ba0 100644
--- a/src/ScriptInterpreter.cpp
+++ b/src/ScriptInterpreter.cpp
@@ -35,8 +35,14 @@
throw std::runtime_error("Double literal out of range: " + token.lexeme);
}
}
+ if (token.type == TokenType::BooleanLiteral || token.type == TokenType::Identifier) {
+ std::string lowered = token.lexeme;
+ std::transform(lowered.begin(), lowered.end(), lowered.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+ return Value::fromBoolean(token, (lowered == "true" ? true : false));
+ }
if (token.type == TokenType::Variable) {
- return this->getVariable(token);
+ return this->getVariable(token, this->contextPrefix, __FILE__, __LINE__);
}
THROW_UNEXPECTED_TOKEN_ERROR(token, "string, integer, double, or variable");
@@ -81,67 +87,79 @@
}
void ScriptInterpreter::handleBooleanDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
- const auto varName = tokens[i].lexeme;
- const auto varType = tokens[i].variableType;
+ const auto & varToken = tokens[i];
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]);
+ const auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
if (variable.type != Variables::Type::VT_BOOLEAN) {
- THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_BOOLEAN),
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme,
+ Variables::TypeToString(Variables::Type::VT_BOOLEAN),
tokens[i].lexeme, variable.TypeToString(), tokens[i]);
}
- this->setVariable(varName, variable);
+ this->setVariable(varToken.lexeme, variable, this->contextPrefix, true);
i++; // Skip variable name
EXPECT_SEMICOLON(tokens, i, "after bool variable declaration");
- } else if (i < tokens.size() && tokens[i].type == TokenType::Identifier) {
+ } else if (i < tokens.size() &&
+ (tokens[i].type == TokenType::Identifier || tokens[i].type == TokenType::StringLiteral)) {
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));
+ this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], true), this->contextPrefix, true);
} else if (lowered == "false") {
- this->setVariable(varName, Value::fromBoolean(tokens[i], false));
+ this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], false), this->contextPrefix, true);
} else {
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "true or false after '='");
}
i++; // Skip boolean literal
EXPECT_SEMICOLON(tokens, i, "after bool declaration");
+ } 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);
+ } else if (test > 0) {
+ this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], true), this->contextPrefix, true);
+ } else {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "bool literal after '='");
+ }
+ i++;
+ 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;
- const auto varType = tokens[i].variableType;
+ const auto varToken = tokens[i];
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]);
+ const auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
if (variable.type != Variables::Type::VT_STRING) {
- THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_STRING),
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme,
+ Variables::TypeToString(Variables::Type::VT_STRING),
tokens[i].lexeme, variable.TypeToString(), tokens[i]);
}
- this->setVariable(varName, variable);
- //variables[varName] = variables[tokens[i].lexeme];
+ this->setVariable(varToken.lexeme, variable, 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(varName, Value::fromString(tokens[i]));
+ this->setVariable(varToken.lexeme, Value::fromString(tokens[i]), this->contextPrefix, true);
i++; // Skip string literal
EXPECT_SEMICOLON(tokens, i, "after string declaration");
} else {
@@ -153,8 +171,7 @@
}
void ScriptInterpreter::handleNumberDeclaration(const std::vector<Token> & tokens, std::size_t & i, TokenType type) {
- const auto varName = tokens[i].lexeme;
- const auto varType = tokens[i].variableType;
+ const auto & varToken = tokens[i];
i++; // Skip variable name
if (i < tokens.size() && tokens[i].type == TokenType::Equals) {
@@ -162,12 +179,7 @@
if (i < tokens.size()) {
if (type == TokenType::IntDeclaration && tokens[i].type == TokenType::IntLiteral) {
try {
- 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]);
- }
- this->setVariable(varName, Value::fromInt(tokens[i]));
+ this->setVariable(varToken.lexeme, Value::fromInt(tokens[i]), 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);
@@ -176,13 +188,7 @@
}
} else if (type == TokenType::DoubleDeclaration && tokens[i].type == TokenType::DoubleLiteral) {
try {
- 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]);
- }
- this->setVariable(varName, Value::fromDouble(tokens[i]));
+ this->setVariable(varToken.lexeme, Value::fromDouble(tokens[i]), 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);
@@ -191,7 +197,7 @@
}
} else {
const std::string expectedType = type == TokenType::IntDeclaration ? "int" : "double";
- THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, expectedType, "",
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme, expectedType, "",
getVariableTypeFromTokenTypeAsString(tokens[i].type), tokens[i]);
}
EXPECT_SEMICOLON(tokens, i, "after variable declaration");
@@ -199,7 +205,7 @@
THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "literal after '='");
}
} else {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after variable declaration, variable name: " + varName);
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after variable declaration, variable name: " + varToken.lexeme);
}
}
@@ -224,24 +230,26 @@
i++;
// parse arg definitions
- const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i);
+ const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i, __FILE__, __LINE__);
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;
+ size_t start;
+ size_t end;
+ ScriptInterpreterHelpers::getFunctionBody(tokens, i, start, end);
+ std::cout << "Body start: " << start << " end: " << end << std::endl;
+ const std::string function_body = ScriptInterpreterHelpers::extractSubstring(this->source, start, end);
+ this->functionBodies[varName] = function_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;
+#if DEBUG_BUILD == 1
+ std::cout << "function body: \n\"" << function_body << "\"" << std::endl;
+#endif
}
void ScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) {
@@ -272,16 +280,13 @@
}
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& varToken = tokens[i];
+ 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()) {
- const auto variable = this->getVariable(varToken);
- this->setVariable(varToken.lexeme, evaluateExpression(tokens[i]));
+ const auto variable = this->getVariable(varToken, this->contextPrefix, __FILE__, __LINE__);
+ this->setVariable(varToken.lexeme, evaluateExpression(tokens[i]), this->contextPrefix, false);
i++; // Skip value
EXPECT_SEMICOLON(tokens, i, "after variable assignment");
} else {
@@ -300,8 +305,11 @@
i++; // Skip semicolon token
}
-void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool ignore_tags) {
- this->filename = filename;
+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;
Lexer lexer(source, filename);
auto tokens = lexer.tokenize();
--
Gitblit v1.9.3