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/ScriptExceptionMacros.h | 40 +
src/Value.hpp | 58 ++
cmake/options.h.in | 12
src/ScriptInterpreterHelpers.hpp | 101 ++++
src/Builtins/MathUtilsModule.hpp | 28 +
src/ScriptInterpreter.cpp | 222 +++++++---
test_scripts/test2.vs | 0
src/BaseFunction.hpp | 68 ++
CMakeLists.txt | 53 +
src/Builtins/SleepModule.hpp | 43 +
src/ScriptInterpreter.hpp | 115 ++---
cli/main.cpp | 23
src/ScriptException.hpp | 124 +++++
src/Lexer.hpp | 27
test_scripts/test1.vs | 0
src/Lexer.cpp | 134 ++++--
src/Builtins/PrintModule.hpp | 51 -
src/Token.hpp | 192 ++++++-
src/VariableTypes.hpp | 7
19 files changed, 990 insertions(+), 308 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95bdf33..d6d4bf2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,32 +9,61 @@
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+set(NEED_CLI ON)
+set(NEED_TESTS OFF)
+set(NEED_STATIC_LIBS ON)
+set(NEED_SHARED_LIBS ON)
-option(BUILD_CLI, "Build example commandline intrepeter" ON)
-option(BUILD_TESTS, "Build tests" OFF)
-option(BUILD_SHARED_LIBS "Build shared library" ON)
-option(BUILD_STATIC_LIBS "Build static library" ON)
+option(BUILD_CLI, "Build example commandline intrepeter" ${NEED_CLI})
+option(BUILD_TESTS, "Build tests" ${NEED_TESTS})
+option(BUILD_SHARED_LIBS "Build shared library" ${NEED_SHARED_LIBS})
+option(BUILD_STATIC_LIBS "Build static library" ${NEED_STATIC_LIBS})
+
+if (BUILD_CLI)
+ set(NEED_CLI ${BUILD_CLI})
+endif()
+if (BUILD_TESTS)
+ set(NEED_TESTS ${BUILD_TESTS})
+endif()
+
+if (BUILD_SHARED_LIBS)
+ set(NEED_SHARED_LIBS ${BUILD_SHARED_LIBS})
+endif()
+
+if (BUILD_STATIC_LIBS)
+ set(NEED_STATIC_LIBS ${BUILD_STATIC_LIBS})
+endif()
set(COMMENT_CHARACTER "#")
+set(PARSER_OPEN_TAG "<?void")
+set(PARSER_CLOSE_TAG "?>")
-message(STATUS "BUILD_CLI: ${BUILD_CLI}")
-message(STATUS "BUILD_TESTS: ${BUILD_TESTS}")
-message(STATUS "\tCOMMENT_CHARACTER: ${COMMENT_CHARACTER}")
+message(STATUS "BUILD_CLI: ${NEED_CLI}")
+message(STATUS "BUILD_TESTS: ${NEED_TESTS}")
+message(STATUS "BUILD_SHARED_LIBS: ${NEED_SHARED_LIBS}")
+message(STATUS "BUILD_STATIC_LIBS: ${NEED_STATIC_LIBS}")
+message(STATUS " COMMENT_CHARACTER: ${COMMENT_CHARACTER}")
+message(STATUS " PARSER_OPEN_TAG: ${PARSER_OPEN_TAG}")
+message(STATUS " PARSER_CLOSE_TAG: ${PARSER_CLOSE_TAG}")
if (CMAKE_BUILD_TYPE STREQUAL "")
- set(CMAKE_BUILD_TYPE Release)
- message(STATUS "CMAKE_BUILD_TYPE is not set, defaulting to Release")
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
set(DEBUG_BUILD ON)
endif()
configure_file("cmake/options.h.in" "include/options.h" @ONLY)
+configure_file("test_scripts/test1.vs" "test_scripts/test1.vs" @ONLY)
+configure_file("test_scripts/test2.vs" "test_scripts/test2.vs" @ONLY)
+
+
include_directories(${CMAKE_BINARY_DIR}/include)
include_directories(src)
@@ -45,12 +74,12 @@
src/Lexer.cpp
)
-if (BUILD_SHARED_LIBS)
+if (NEED_SHARED_LIBS)
add_library(${CMAKE_PROJECT_NAME} SHARED)
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${SOURCES})
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME})
endif()
-if (BUILD_STATIC_LIBS)
+if (NEED_STATIC_LIBS)
add_library(${CMAKE_PROJECT_NAME}_static STATIC)
target_sources(${CMAKE_PROJECT_NAME}_static PRIVATE ${SOURCES})
set_target_properties(${CMAKE_PROJECT_NAME}_static PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME})
@@ -59,7 +88,7 @@
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
-if (BUILD_CLI)
+if (NEED_CLI)
add_executable(cli cli/main.cpp)
add_dependencies(cli ${CMAKE_PROJECT_NAME})
if (BUILD_SHARED_LIBS)
diff --git a/cli/main.cpp b/cli/main.cpp
index 882134c..1ec1928 100644
--- a/cli/main.cpp
+++ b/cli/main.cpp
@@ -2,6 +2,7 @@
#include <fstream>
#include "Builtins/PrintModule.hpp"
+#include "Builtins/SleepModule.hpp"
#include "ScriptInterpreter.hpp"
static bool DEBUG = false;
@@ -15,9 +16,20 @@
std::string file;
if (argc == 2) {
file = argv[1];
- } else if (argc == 3 && (std::string(argv[1]) == "-d" || std::string(argv[1]) == "--debug")) {
- DEBUG = true;
- file = argv[2];
+ } else if (argc == 3) {
+ if (std::string(argv[1]) == "-d" || std::string(argv[1]) == "--debug") {
+ DEBUG = true;
+ file = argv[2];
+ } else if (argv[1] == "-h" || argv[1] == "--help") {
+ std::cout << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n";
+ return 0;
+ } else if (argv[1] == "-v" || argv[1] == "--vrsion") {
+ std::cout << "VoidScript v" << VERSION_STRING << "\n";
+ return 0;
+ } else {
+ std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n";
+ return 1;
+ }
} else {
std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n";
return 1;
@@ -37,9 +49,10 @@
return 1;
}
- std::string content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
+ std::string content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
ScriptInterpreter interp;
- interp.registerFunction("print", std::make_shared<PrintFunction>());
+ interp.registerModule("print", std::make_shared<PrintFunction>());
+ interp.registerModule("sleep", std::make_shared<SleepFunction>());
interp.executeScript(content, filename, DEBUG);
} catch (const std::exception & e) {
std::cerr << "Parser error: " << e.what() << "\n";
diff --git a/cmake/options.h.in b/cmake/options.h.in
index 384a0f4..c457729 100644
--- a/cmake/options.h.in
+++ b/cmake/options.h.in
@@ -1,3 +1,7 @@
+
+
+#ifndef VOIDSCRIPT_OPTIONS_H
+#define VOIDSCRIPT_OPTIONS_H
/*
* THIS IS A GENERATED FILE. DO NOT EDIT.
*/
@@ -7,10 +11,12 @@
const char EOL = '\n';
#endif
const char COMMENT_CHARACTER = '@COMMENT_CHARACTER@';
-const static char * PARSER_OPEN_TAG = "<?void";
-const static char * PARSER_CLOSE_TAG = "?>";
+const static char * PARSER_OPEN_TAG = "@PARSER_OPEN_TAG@";
+const static char * PARSER_CLOSE_TAG = "@PARSER_CLOSE_TAG@";
const static char * VERSION_MINOR = "@CMAKE_PROJECT_VERSION_MINOR@";
const static char * VERSION_MAJOR = "@CMAKE_PROJECT_VERSION_MAJOR@";
const static char * VERSION_PATCH = "@CMAKE_PROJECT_VERSION_PATCH@";
const static char * VERSION_STRING = "@CMAKE_PROJECT_VERSION@-@CMAKE_BUILD_TYPE@-@CMAKE_SYSTEM@";
-#cmakedefine DEBUG_BUILD
+#cmakedefine01 DEBUG_BUILD
+
+#endif // VOIDSCRIPT_OPTIONS_H
\ No newline at end of file
diff --git a/src/BaseFunction.hpp b/src/BaseFunction.hpp
index a870da6..9038dbc 100644
--- a/src/BaseFunction.hpp
+++ b/src/BaseFunction.hpp
@@ -1,26 +1,78 @@
-// ScriptFunction.hpp
#ifndef SCRIPT_FUNCTION_HPP
#define SCRIPT_FUNCTION_HPP
+#include <functional>
+#include <stdexcept>
+#include <unordered_map>
+#include <utility>
#include <vector>
+#include "ScriptExceptionMacros.h"
+#include "ScriptInterpreterHelpers.hpp"
#include "Token.hpp"
#include "Value.hpp"
-
class ScriptInterpreter;
+
+using CallbackFunction = std::function<Value(const std::vector<Value> &)>;
+using CallBackStorage = std::unordered_map<std::string, CallbackFunction>;
class BaseFunction {
protected:
- std::string name;
+ std::string name;
+ CallBackStorage functionMap;
+
+
public:
- virtual ~BaseFunction() = default;
- virtual void validate(const std::vector<Token> & tokens, size_t & i) const = 0;
+ BaseFunction(const std::string & functionName) : name(functionName) {}
+
+ virtual void addFunction(const std::string & name, std::function<Value(const std::vector<Value> &)> callback) {
+ functionMap[name] = std::move(callback);
+ }
+
+ virtual void validate(const std::vector<Token> & tokens, size_t & i,
+ const std::unordered_map<std::string, Value> & variables) {
+ size_t index = i;
+
+ if (tokens[index].type != TokenType::Identifier) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "identifier");
+ }
+ index++; // skip function name
+
+ if (tokens[index].type != TokenType::LeftParenthesis) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "(");
+ }
+ index++; // skip '('
+
+ std::vector<Token> args;
+ while (tokens[index].type != TokenType::RightParenthesis) {
+ if (tokens[index].type == TokenType::Comma) {
+ index++;
+ continue;
+ }
+ if (tokens[index].type == TokenType::Variable && !variables.contains(tokens[index].lexeme)) {
+ THROW_UNDEFINED_VARIABLE_ERROR(tokens[index].lexeme, tokens[index]);
+ }
+ args.push_back(tokens[index]);
+ index++;
+ }
+
+ index++; // skip ')'
+
+ if (tokens[index].type != TokenType::Semicolon) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], ";");
+ }
+
+ this->validateArgs(args, variables);
+ ScriptInterpreterHelpers::expectSemicolon(tokens, index, "function call");
+ }
+
+ virtual void validateArgs(const std::vector<Token> & args,
+ const std::unordered_map<std::string, Value> & variables) = 0;
+
virtual Value call(const std::vector<Value> & args, bool debug = false) const = 0;
- template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) {
- FuncClass::registerTo(interp);
- }
+ template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) { FuncClass::registerTo(interp); }
};
#endif // SCRIPT_FUNCTION_HPP
diff --git a/src/Builtins/MathUtilsModule.hpp b/src/Builtins/MathUtilsModule.hpp
new file mode 100644
index 0000000..e85f4d8
--- /dev/null
+++ b/src/Builtins/MathUtilsModule.hpp
@@ -0,0 +1,28 @@
+#ifndef MATH_UTILS_MODULE_HPP
+#define MATH_UTILS_MODULE_HPP
+
+#include <stdexcept>
+#include <vector>
+
+#include "ScriptExceptionMacros.h"
+#include "Value.hpp"
+
+class MathUtils {
+ public:
+ static Value multiply(const std::vector<Value> & args) {
+ if (args.size() != 2) {
+ throw std::runtime_error("multiply expects two arguments.");
+ }
+ if (args[0].type == Variables::Type::VT_INT && args[1].type == Variables::Type::VT_INT) {
+ int left = args[0].ToInt();
+ int right = args[1].ToInt();
+ auto result = Value();
+ result.data = left * right;
+ result.type = Variables::Type::VT_INT;
+ return result;
+ //return Value::fromInt(left * right);
+ }
+ THROW_INVALID_FUNCTION_ARGUMENT_ERROR("multiply", args[0].TypeToString(), args[0].GetToken());
+ };
+};
+#endif
diff --git a/src/Builtins/PrintModule.hpp b/src/Builtins/PrintModule.hpp
index 0b4ad56..a7bc07d 100644
--- a/src/Builtins/PrintModule.hpp
+++ b/src/Builtins/PrintModule.hpp
@@ -5,54 +5,37 @@
#include "BaseFunction.hpp"
#include "ScriptExceptionMacros.h"
-#include "ScriptInterpreter.hpp"
#include "Token.hpp"
#include "Value.hpp"
class PrintFunction : public BaseFunction {
private:
- const std::string name = "print";
+ const std::string name = "print";
+ bool addNewLine = false;
public:
- void validate(const std::vector<Token> & tokens, size_t & i) const override {
- auto index = i;
- if (tokens[index].type != TokenType::Identifier) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "identifier: " + name);
+ PrintFunction() : BaseFunction(name) {}
+
+ void validateArgs(const std::vector<Token> & args,
+ const std::unordered_map<std::string, Value> & variables) override {
+ if (args.size() == 0) {
+ THROW_UNEXPECTED_TOKEN_ERROR(args[0], "at least one argument");
}
- index++; // skip function name
- if (tokens[index].type != TokenType::LeftParenthesis) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "('");
- }
- index++; // skip '('
- if (tokens[index].type != TokenType::StringLiteral && tokens[index].type != TokenType::Variable &&
- tokens[index].type != TokenType::IntLiteral && tokens[index].type != TokenType::DoubleLiteral) {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "string, int, double or variable as argument");
- }
- size_t count = 0;
- while (tokens[index].type != TokenType::RightParenthesis) {
- if (tokens[index].type == TokenType::StringLiteral || tokens[index].type == TokenType::Variable ||
- tokens[index].type == TokenType::IntLiteral || tokens[index].type == TokenType::DoubleLiteral) {
- count++;
- index++;
- } else if (tokens[index].type == TokenType::Comma) {
- index++;
- } else {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "string, int, double or variable as argument");
+
+ for (const auto & arg : args) {
+ if (arg.type == TokenType::Variable) {
+ if (!variables.contains(arg.lexeme)) {
+ THROW_UNDEFINED_VARIABLE_ERROR(arg.lexeme, arg);
+ }
}
}
- if (count == 0) {
- throw std::runtime_error("print() requires at least one argument at");
- }
- index++; // skip ')'
- if (tokens[index].type == TokenType::Semicolon) {
- index++;
- } else {
- THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], ";");
+ if (args.end()->variableType == Variables::Type::VT_INT || args.end()->type == TokenType::IntLiteral) {
+ this->addNewLine = true;
}
}
Value call(const std::vector<Value> & args, bool debug = false) const override {
for (const auto & arg : args) {
- std::cout << arg.ToString();
+ std::cout << arg.ToString(); // todo: add endline if the last parameter is bool
}
return Value();
}
diff --git a/src/Builtins/SleepModule.hpp b/src/Builtins/SleepModule.hpp
new file mode 100644
index 0000000..17585b2
--- /dev/null
+++ b/src/Builtins/SleepModule.hpp
@@ -0,0 +1,43 @@
+#ifndef SLEEPFUNCTION_HPP
+#define SLEEPFUNCTION_HPP
+
+#include <thread>
+
+#include "BaseFunction.hpp"
+
+class SleepFunction : public BaseFunction {
+ public:
+ SleepFunction() : BaseFunction("sleep") {}
+
+ void validateArgs(const std::vector<Token> & args,
+ const std::unordered_map<std::string, Value> & variables) override {
+ if (args.size() != 1) {
+ throw std::runtime_error("sleep() requires exactly one argument");
+ }
+
+ const Token & arg = args[0];
+
+ if (arg.type == TokenType::IntLiteral) {
+ return;
+ }
+
+ if (arg.type == TokenType::Variable) {
+ const auto & value = variables.at(arg.lexeme);
+ if (value.type != Variables::Type::VT_INT) {
+ THROW_VARIABLE_TYPE_MISSMATCH_ERROR(arg.lexeme, Variables::TypeToString(Variables::Type::VT_INT), "",
+ Variables::TypeToString(value.type), arg);
+ }
+ return;
+ }
+
+ THROW_UNEXPECTED_TOKEN_ERROR(arg, "int literal or variable");
+ }
+
+
+ Value call(const std::vector<Value> & args, bool debug = false) const override {
+ std::this_thread::sleep_for(std::chrono::seconds(args[0].ToInt()));
+ return Value();
+ }
+};
+
+#endif // SLEEPFUNCTION_HPP
diff --git a/src/Lexer.cpp b/src/Lexer.cpp
index 62e110c..c7067d7 100644
--- a/src/Lexer.cpp
+++ b/src/Lexer.cpp
@@ -2,7 +2,7 @@
#include <cctype>
-#include "Value.hpp"
+#include "options.h"
Lexer::Lexer(const std::string & source, const std::string & filename) :
src(source),
@@ -128,17 +128,18 @@
while (isalpha(peek())) {
lexeme += advance();
}
-
- if (Variables::StringToTypeMap.contains(lexeme)) {
- const auto type = Variables::StringToTypeMap.at(lexeme);
+ auto it = Variables::StringToTypeMap.find(lexeme);
+ if (it != Variables::StringToTypeMap.end()) {
+ const auto & type = it->second;
while (isspace(peek())) {
advance();
}
if (peek() == '$') {
- return variableDeclaration(type);
+ return this->variableDeclaration(type);
}
return { TokenType::Identifier, lexeme, filename, lineNumber, startCol };
}
+
return { TokenType::Identifier, lexeme, filename, lineNumber, startCol };
}
@@ -151,19 +152,15 @@
while (isalnum(peek()) || peek() == '_') {
varName += advance();
}
- switch (type) {
- case Variables::Type::VT_INT:
- return { TokenType::IntDeclaration, varName, filename, lineNumber, startCol };
- case Variables::Type::VT_DOUBLE:
- return { TokenType::DoubleDeclaration, varName, filename, lineNumber, startCol };
- case Variables::Type::VT_STRING:
- return { TokenType::StringDeclaration, varName, filename, lineNumber, startCol };
- default:
- return { TokenType::Unknown, "Invalid variable type in declaration", filename, lineNumber, startCol };
+ for (auto it = Variables::StringToTypeMap.begin(); it != Variables::StringToTypeMap.end(); ++it) {
+ if (it->second == type) {
+ return { getTokenTypeFromValueDeclaration(it->second), varName, filename, lineNumber, startCol };
+ }
}
- } else {
- return { TokenType::Unknown, "$ followed by invalid character in declaration", filename, lineNumber, startCol };
+
+ return { TokenType::Unknown, "Invalid variable type in declaration", filename, lineNumber, startCol };
}
+ return { TokenType::Unknown, "$ followed by invalid character in declaration", filename, lineNumber, startCol };
}
Token Lexer::singleCharToken(TokenType type, const std::string & lexeme) {
@@ -172,15 +169,30 @@
return { type, lexeme, filename, lineNumber, startCol };
}
-bool Lexer::matchSequence(const std::string & sequence) const {
- if (pos + sequence.length() > src.length()) {
+bool Lexer::matchSequence(const std::string & sequence, bool caseSensitive) const {
+ if (this->pos + sequence.size() > src.size()) {
return false;
}
- return src.substr(pos, sequence.length()) == sequence;
+
+ for (size_t i = 0; i < sequence.size(); ++i) {
+ char srcChar = src[this->pos + i];
+ char seqChar = sequence[i];
+
+ if (!caseSensitive) {
+ srcChar = std::tolower(static_cast<unsigned char>(srcChar));
+ seqChar = std::tolower(static_cast<unsigned char>(seqChar));
+ }
+
+ if (srcChar != seqChar) {
+ return false;
+ }
+ }
+
+ return true;
}
-void Lexer::matchAndConsume(const std::string & sequence) {
- if (matchSequence(sequence)) {
+void Lexer::matchAndConsume(const std::string & sequence, bool caseSensitive) {
+ if (matchSequence(sequence, caseSensitive)) {
for (size_t i = 0; i < sequence.length(); ++i) {
advance();
}
@@ -189,18 +201,19 @@
std::vector<Token> Lexer::tokenize() {
std::vector<Token> tokens;
+ tokens.reserve(src.size() / 4);
- while (!isAtEnd()) {
- char c = peek();
+ while (pos < src.size()) {
+ char c = src[pos];
if (isspace(c)) {
advance();
continue;
}
if (c == '\n') {
- tokens.push_back(singleCharToken(TokenType::EndOfLine, ""));
+ tokens.push_back(singleCharToken(TokenType::EndOfLine, "\n"));
continue;
}
- if (c == '#') {
+ if (c == COMMENT_CHARACTER) {
tokens.push_back(comment());
advance(); // Skip newline after comment
continue;
@@ -217,27 +230,56 @@
tokens.push_back({ TokenType::ParserCloseTag, PARSER_CLOSE_TAG, filename, lineNumber, startCol });
continue;
}
- if (isalpha(c)) {
- tokens.push_back(keywordOrIdentifier());
- } else if (c == '$') {
- tokens.push_back(variable());
- } else if (isdigit(c)) {
- tokens.push_back(number());
- } else if (c == '"' || c == '\'') {
- tokens.push_back(string());
- } else if (c == '(') {
- tokens.push_back(singleCharToken(TokenType::LeftParenthesis, "("));
- } else if (c == ')') {
- tokens.push_back(singleCharToken(TokenType::RightParenthesis, ")"));
- } else if (c == ',') {
- tokens.push_back(singleCharToken(TokenType::Comma, ","));
- } else if (c == ';') {
- tokens.push_back(singleCharToken(TokenType::Semicolon, ";"));
- } else if (c == '=') {
- tokens.push_back(singleCharToken(TokenType::Equals, "="));
- } else {
- tokens.push_back({ TokenType::Unknown, std::string(1, c), filename, lineNumber, colNumber });
- advance();
+ if (matchSequence("if")) {
+ size_t startCol = colNumber;
+ matchAndConsume("if");
+ tokens.push_back({ TokenType::ParserIfStatement, "if", filename, lineNumber, startCol });
+ continue;
+ }
+
+ switch (c) {
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ tokens.push_back(keywordOrIdentifier());
+ break;
+ case '$':
+ tokens.push_back(variable());
+ break;
+ case '0' ... '9':
+ tokens.push_back(number());
+ break;
+ case '"':
+ case '\'':
+ tokens.push_back(string());
+ break;
+ case '(':
+ tokens.push_back(singleCharToken(TokenType::LeftParenthesis, "("));
+ break;
+ case ')':
+ tokens.push_back(singleCharToken(TokenType::RightParenthesis, ")"));
+ break;
+ case ',':
+ tokens.push_back(singleCharToken(TokenType::Comma, ","));
+ break;
+ case ';':
+ tokens.push_back(singleCharToken(TokenType::Semicolon, ";"));
+ break;
+ case '=':
+ tokens.push_back(singleCharToken(TokenType::Equals, "="));
+ break;
+ case '+':
+ tokens.push_back(singleCharToken(TokenType::Plus, "+"));
+ break;
+ case '{':
+ tokens.push_back(singleCharToken(TokenType::LeftCurlyBracket, "{"));
+ break;
+ case '}':
+ tokens.push_back(singleCharToken(TokenType::RightCurlyBracket, "}"));
+ break;
+ default:
+ tokens.push_back({ TokenType::Unknown, std::string(1, c), filename, lineNumber, colNumber });
+ advance();
+ break;
}
}
diff --git a/src/Lexer.hpp b/src/Lexer.hpp
index b5ac4e3..d374fa1 100644
--- a/src/Lexer.hpp
+++ b/src/Lexer.hpp
@@ -1,13 +1,13 @@
#ifndef LEXER_HPP
#define LEXER_HPP
+#include <algorithm>
#include <istream>
#include <sstream>
#include <vector>
-#include "VariableTypes.hpp"
-#include "options.h"
#include "Token.hpp"
+#include "VariableTypes.hpp"
class Lexer {
public:
@@ -32,11 +32,11 @@
Token variable();
Token comment();
Token keywordOrIdentifier();
+ Token boolean();
Token singleCharToken(TokenType type, const std::string & lexeme);
- bool matchSequence(const std::string & sequence) const;
+ bool matchSequence(const std::string & sequence, bool caseSensitive = true) const;
Token variableDeclaration(Variables::Type type);
- void matchAndConsume(const std::string & sequence);
-
+ void matchAndConsume(const std::string & sequence, bool caseSensitive = true);
// validate number types from string
template <typename Numeric> static bool is_number(const std::string & s) {
@@ -44,7 +44,22 @@
return ((std::istringstream(s) >> n >> std::ws).eof());
}
- bool matchSequence(const std::string & sequence) { return src.substr(pos, sequence.length()) == sequence; }
+ bool matchSequence(const std::string & sequence, bool caseSensitive = true) {
+ if (caseSensitive) {
+ return src.substr(pos, sequence.length()) == sequence;
+ }
+
+ std::string srcSubstr = src.substr(pos, sequence.length());
+ std::string seqLower = sequence;
+
+ std::transform(srcSubstr.begin(), srcSubstr.end(), srcSubstr.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+
+ std::transform(seqLower.begin(), seqLower.end(), seqLower.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+
+ return srcSubstr == seqLower;
+ }
};
#endif // LEXER_HPP
diff --git a/src/ScriptException.hpp b/src/ScriptException.hpp
new file mode 100644
index 0000000..2d7d555
--- /dev/null
+++ b/src/ScriptException.hpp
@@ -0,0 +1,124 @@
+#ifndef SCRIPTEXCEPTION_HPP
+#define SCRIPTEXCEPTION_HPP
+
+#include <stdexcept>
+#include <string>
+
+#include "options.h"
+#include "Token.hpp"
+
+enum class ScriptErrorType : std::uint8_t {
+ UnexpectedToken,
+ UndefinedVariable,
+ UndefinedFunction,
+ VariableTypeMismatch,
+ VariableRedefinition,
+ Custom
+};
+
+class ScriptException : public std::runtime_error {
+ public:
+ ScriptException(ScriptErrorType type, const std::string & message, const std::string & file = "", int line = 0,
+ const Token & token = Token()) :
+ std::runtime_error(message),
+ type_(type),
+ file_(file),
+ line_(line),
+ token_(token),
+ fullMessage_(formatMessage(message)) {}
+
+ const char * what() const noexcept override { return fullMessage_.c_str(); }
+
+ ScriptErrorType type() const { return type_; }
+
+ const std::string & file() const { return file_; }
+
+ int line() const { return line_; }
+
+ const Token & token() const { return token_; }
+
+ static ScriptException makeUnexpectedTokenError(const Token & token, const std::string & expected = "",
+ const std::string & file = "", int line = 0) {
+ std::string msg = "unexpected token: '" + token.lexeme + "'";
+
+#if DEBUG_BUILD == 1
+ msg.append(" token type: " + tokenTypeNames.at(token.type));
+#endif
+
+ if (!expected.empty()) {
+ msg += ", expected: '" + expected + "'";
+ }
+ return ScriptException(ScriptErrorType::UnexpectedToken, msg, file, line, token);
+ }
+
+ static ScriptException makeUndefinedVariableError(const std::string & name, const Token & token,
+ const std::string & file = "", int line = 0) {
+ std::string msg = "undefined variable: '$" + name + "'";
+ return ScriptException(ScriptErrorType::UndefinedVariable, msg, file, line, token);
+ }
+
+ static ScriptException makeUndefinedFunctionError(const std::string & name, const Token & token,
+ const std::string & file = "", int line = 0) {
+ std::string msg = "undefined function: '" + name + "'";
+#if DEBUG_BUILD == 1
+ msg.append(", type: " + tokenTypeNames.at(token.type));
+#endif
+ return ScriptException(ScriptErrorType::UndefinedFunction, msg, file, line, token);
+ }
+
+ static ScriptException makeVariableRedefinitionError(const std::string & name, const Token & token,
+ const std::string & file = "", int line = 0) {
+ std::string msg = "variable already defined: '" + name + "'";
+ return ScriptException(ScriptErrorType::VariableRedefinition, msg, file, line, token);
+ }
+
+ static ScriptException makeVariableTypeMismatchError(const std::string & targetVar, const std::string & targetType,
+ const std::string & sourceVar, const std::string & sourceType,
+ const Token & token, const std::string & file = "",
+ int line = 0) {
+ std::string msg = "variable type mismatch: '$" + targetVar + "' declared type: '" + targetType + "'";
+ if (!sourceVar.empty()) {
+ msg += ", source variable: '" + sourceVar + "'";
+ }
+ if (!sourceType.empty()) {
+ msg += ", assigned type: '" + sourceType + "'";
+ }
+ return ScriptException(ScriptErrorType::VariableTypeMismatch, msg, file, line, token);
+ }
+
+ static ScriptException makeFunctionRedefinitionError(const std::string & name, const Token & token,
+ const std::string & file = "", int line = 0) {
+ std::string msg = "variable already defined: '" + name + "'";
+ return ScriptException(ScriptErrorType::VariableRedefinition, msg, file, line, token);
+ }
+
+ static ScriptException makeFunctionInvalidArgumentError(const std::string & functionName,
+ const std::string & argName, const Token & token,
+ const std::string & file = "", int line = 0) {
+ std::string msg = "invalid argument for function '" + functionName + "': '" + argName + "'";
+ return ScriptException(ScriptErrorType::Custom, msg, file, line, token);
+ }
+
+ private:
+ ScriptErrorType type_;
+ std::string file_;
+ int line_;
+ Token token_;
+ std::string fullMessage_;
+
+ std::string formatMessage(const std::string & base) const {
+ std::string formatted = base;
+ if (!token_.file.empty()) {
+ formatted += " in file: " + token_.file + ":" + std::to_string(token_.lineNumber) + ":" +
+ std::to_string(token_.columnNumber);
+ }
+#if DEBUG_BUILD == 1
+ if (!file_.empty()) {
+ formatted = file_ + ":" + std::to_string(line_) + "\n" + formatted;
+ }
+#endif
+ return formatted;
+ }
+};
+
+#endif // SCRIPTEXCEPTION_HPP
diff --git a/src/ScriptExceptionMacros.h b/src/ScriptExceptionMacros.h
index 31fa7da..c2e754f 100644
--- a/src/ScriptExceptionMacros.h
+++ b/src/ScriptExceptionMacros.h
@@ -1,19 +1,43 @@
#ifndef SCRIPT_EXCEPTION_MACROS_H
#define SCRIPT_EXCEPTION_MACROS_H
+#include "ScriptException.hpp"
+
+//
+// Purpose of macros: unified exception handling with extended error information (source file and line number)
+//
+
+// Invalid token type - expected different type
#define THROW_UNEXPECTED_TOKEN_ERROR(token, expected) \
- ScriptInterpreter::throwUnexpectedTokenError(token, expected, __FILE__, __LINE__)
+ throw ScriptException::makeUnexpectedTokenError(token, expected, __FILE__, __LINE__)
+#define THROW_UNEXPECTED_TOKEN_ERROR_HELPER(token, expected, file, line) \
+ throw ScriptException::makeUnexpectedTokenError(token, expected, file, line)
+
+// Accessing unknown (undefined) variable
#define THROW_UNDEFINED_VARIABLE_ERROR(name, token) \
- ScriptInterpreter::throwUndefinedVariableError(name, token, __FILE__, __LINE__)
+ throw ScriptException::makeUndefinedVariableError(name, token, __FILE__, __LINE__)
-#define THROW_VARIABLE_TYPE_MISSMATCH_ERROR(target_variable_name, target_variable_type, source_variable_name, \
- source_variable_type, token) \
- ScriptInterpreter::throwVariableTypeMissmatchError(target_variable_name, target_variable_type, \
- source_variable_name, source_variable_type, token, __FILE__, \
- __LINE__)
+// Unknown (undefined) function call
+#define THROW_UNDEFINED_FUNCTION_ERROR(name, token) \
+ throw ScriptException::makeUndefinedFunctionError(name, token, __FILE__, __LINE__)
+// Variable type mismatch - e.g. string instead of number
+#define THROW_VARIABLE_TYPE_MISSMATCH_ERROR(target_variable_name, target_variable_type, source_variable_name, \
+ source_variable_type, token) \
+ throw ScriptException::makeVariableTypeMismatchError(target_variable_name, target_variable_type, \
+ source_variable_name, source_variable_type, token, __FILE__, \
+ __LINE__)
+
+// Redefining a variable with the same name is not allowed
#define THROW_VARIABLE_REDEFINITION_ERROR(name, token) \
- ScriptInterpreter::throwVariableRedefinitionError(name, token, __FILE__, __LINE__)
+ throw ScriptException::makeVariableRedefinitionError(name, token, __FILE__, __LINE__)
+
+#define THROW_FUNCTION_REDEFINITION_ERROR(name, token) \
+ throw ScriptException::makeFunctionRedefinitionError(name, token, __FILE__, __LINE__)
+
+// Invalid or incorrect function argument
+#define THROW_INVALID_FUNCTION_ARGUMENT_ERROR(functionName, argName, token) \
+ throw ScriptException::makeFunctionInvalidArgumentError(functionName, argName, token, __FILE__, __LINE__)
#endif // SCRIPT_EXCEPTION_MACROS_H
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, "");
}
}
}
diff --git a/src/ScriptInterpreter.hpp b/src/ScriptInterpreter.hpp
index 2791e02..5ecc7c3 100644
--- a/src/ScriptInterpreter.hpp
+++ b/src/ScriptInterpreter.hpp
@@ -1,6 +1,7 @@
#ifndef SSCRIPTINTERPRETER_HPP
#define SSCRIPTINTERPRETER_HPP
#include <functional>
+#include <map>
#include <memory>
#include <string>
#include <unordered_map>
@@ -10,90 +11,66 @@
#include "Token.hpp"
#include "Value.hpp"
-using FunctionValidator = std::function<void(const std::vector<Token> &, size_t &)>;
+using FunctionValidator =
+ std::function<void(const std::vector<Token> &, size_t &, const std::unordered_map<std::string, Value> &)>;
+using VariableContext = std::unordered_map<std::string, Value>;
class ScriptInterpreter {
public:
- void registerFunction(const std::string & name, std::shared_ptr<BaseFunction> fn);
- void executeScript(const std::string & source, const std::string & filenaneame, bool debug = false);
-
- static void throwUnexpectedTokenError(const Token & token, const std::string & expected = "",
- const std::string & file = "", const int & line = 0) {
- const std::string error_content =
- "unexpected token: '" + token.lexeme + "' type: " + tokenTypeNames.at(token.type) +
- (expected.empty() ? "" : ", expected: '" + expected + "'") + " in file: " + token.file + ":" +
- std::to_string(token.lineNumber) + ":" + std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
- const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
- const std::string& error_message = error_content;
-#endif
- throw std::runtime_error(error_message);
- };
-
- static void throwUndefinedVariableError(const std::string & name, const Token & token, const std::string & file,
- const int & line = 0) {
- const std::string error_content = "undefined variable: '$" + name + "' in file: " + token.file + ":" +
- std::to_string(token.lineNumber) + ":" + std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
- const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
- const std::string& error_message = error_content;
-#endif
- throw std::runtime_error(error_message);
- }
-
- static void throwVariableTypeMissmatchError(const std::string & target_variable_name,
- const std::string & target_type,
- const std::string & source_variable_name,
- const std::string & source_type, const Token & token,
- const std::string & file = "", const int & line = 0) {
- std::string error_content =
- "variable type missmatch: '$" + target_variable_name + "' declared type: '" + target_type + "'";
- if (!source_variable_name.empty()) {
- error_content += " source variable: '" + source_variable_name + "'";
- }
- if (!source_type.empty()) {
- error_content += " assigned type: '" + source_type + "'";
- }
-
- error_content += " in file: " + token.file + ":" + std::to_string(token.lineNumber) + ":" +
- std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
- const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
- const std::string error_message = error_content;
-#endif
- throw std::runtime_error(error_message);
- }
-
- static void throwVariableRedefinitionError(const std::string & name, const Token & token,
- const std::string & file = "", const int line = 0) {
- const std::string error_content = "variable alread defined: " + name + " in file: " + token.file + ":" +
- std::to_string(token.lineNumber) + ":" + std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
- const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
- const std::string& error_message = error_content;
-#endif
- throw std::runtime_error(error_message);
- }
+ void registerModule(const std::string & name, std::shared_ptr<BaseFunction> fn);
+ void executeScript(const std::string & source, const std::string & filenaneame, bool ignore_tags = false);
private:
std::unordered_map<std::string, FunctionValidator> functionValidators;
std::unordered_map<std::string, std::shared_ptr<BaseFunction>> functionObjects;
- std::unordered_map<std::string, Value> variables;
+ // store the current script's variables
+ std::unordered_map<std::string, VariableContext> variables;
+ // store the current script's function arguments
+ std::unordered_map<std::string, std::vector<Value>> functionParameters;
+ std::unordered_map<std::string, std::string> functionBodies;
+ std::string filename;
- std::vector<Value> parseArguments(const std::vector<Token> & tokens, std::size_t & current_index) const;
- Value evaluateExpression(const Token & token) const;
+ [[nodiscard]] std::vector<Value> parseFunctionArguments(const std::vector<Token> & tokens,
+ std::size_t & index) const;
+ [[nodiscard]] Value evaluateExpression(const Token & token) const;
+
// type handlers
- void expectSemicolon(const std::vector<Token> & tokens, std::size_t & i, const std::string & message) const;
+ void setVariable(const std::string & name, const Value & value, const std::string & context = "default") {
+ this->variables[context][name] = value;
+ }
+
+ [[nodiscard]] Value getVariable(const std::string & name, const std::string & context = "default") const {
+ for (auto it = variables.begin(); it != variables.end(); ++it) {
+ if (it->first == context) {
+ const auto & innerMap = it->second.find(name);
+ if (innerMap != it->second.end()) {
+ return it->second.at(name);
+ }
+ }
+ }
+ throw std::runtime_error("Variable not found: " + name);
+ };
+
+ [[nodiscard]] Value getVariable(const Token & token, const std::string & context = "default") const {
+ for (auto it = variables.begin(); it != variables.end(); ++it) {
+ if (it->first == context) {
+ const auto & innerMap = it->second.find(token.lexeme);
+ if (innerMap != it->second.end()) {
+ return it->second.at(token.lexeme);
+ }
+ }
+ }
+ THROW_UNDEFINED_VARIABLE_ERROR(token.lexeme, token);
+ };
+
void handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i);
void handleVariableReference(const std::vector<Token> & tokens, std::size_t & i);
void handleComment(std::size_t & i);
void handleSemicolon(std::size_t & i);
void handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i);
+ void handleBooleanDeclaration(const std::vector<Token> & tokens, std::size_t & i);
void handleNumberDeclaration(const std::vector<Token> & tokens, std::size_t & i, TokenType type);
+ void handleFunctionDeclaration(const std::vector<Token> & tokens, std::size_t & i);
};
#endif // SSCRIPTINTERPRETER_HPP
diff --git a/src/ScriptInterpreterHelpers.hpp b/src/ScriptInterpreterHelpers.hpp
new file mode 100644
index 0000000..6b615c7
--- /dev/null
+++ b/src/ScriptInterpreterHelpers.hpp
@@ -0,0 +1,101 @@
+#ifndef SCRIPTINTERPRETERHELPERS_HPP
+#define SCRIPTINTERPRETERHELPERS_HPP
+
+#include <iostream>
+#include <ostream>
+#include <vector>
+
+#include "ScriptExceptionMacros.h"
+#include "Token.hpp"
+#include "Value.hpp"
+
+#define EXPECT_SEMICOLON(tokens, i, message) \
+ ScriptInterpreterHelpers::expectSemicolon(tokens, i, message, __FILE__, __LINE__)
+
+namespace ScriptInterpreterHelpers {
+
+static std::string extractSubstring(const std::string & str, size_t start, size_t end) {
+ if (start >= 0 && start < str.length() && end >= start && end < str.length()) {
+ return str.substr(start, end - start + 1);
+ }
+ return "";
+}
+
+static void expectSemicolon(const std::vector<Token> & tokens, std::size_t & i, const std::string & message,
+ const std::string & file = __FILE__, int line = __LINE__) {
+ if (i >= tokens.size() || tokens[i].type != TokenType::Semicolon) {
+ THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i - 1], "; " + message, file, line);
+ }
+ i++; // Skip ';'
+}
+
+[[nodiscard]] static std::vector<Value> parseFunctionDeclarationArguments(const std::vector<Token> & tokens,
+ std::size_t & i,
+ const std::string & file = __FILE__,
+ int line = __LINE__) {
+ std::vector<Value> arguments;
+
+ // check the arguments types
+ if (tokens[i].type != TokenType::StringDeclaration && tokens[i].type != TokenType::BooleanDeclaration &&
+ tokens[i].type != TokenType::IntDeclaration && tokens[i].type != TokenType::DoubleDeclaration) {
+ THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "variable declaration", file, line);
+ }
+ const auto parameter_type = getVariableTypeFromTokenTypeDeclaration(tokens[i].type);
+ if (parameter_type == Variables::Type::VT_NOT_DEFINED) {
+ THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "valid type identifier", file, line);
+ }
+
+ if (parameter_type == Variables::Type::VT_FUNCTION) {
+ THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "valid type identifier", file, line);
+ }
+
+ if (parameter_type == Variables::Type::VT_NULL) {
+ THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "valid type identifier", file, line);
+ }
+
+ Value val;
+ val.type = parameter_type;
+ val.token = tokens[i];
+
+ arguments.emplace_back(std::move(val));
+ i++; // Skip variable declaration
+
+ if (tokens[i].type != TokenType::RightParenthesis) {
+ THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], ") - Only one argument is allowed", file, line);
+ }
+ i++; // Skip ')'
+
+ return arguments;
+}
+
+[[nodiscard]] static std::string getFunctionBody(const std::vector<Token> & tokens, std::size_t & i) {
+ const size_t first_index = i;
+ if (i >= tokens.size() || tokens[i].type != TokenType::LeftCurlyBracket) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "{");
+ }
+ i++; // Skip '{'
+ std::string lines;
+
+ while (i < tokens.size() && tokens[i].type != TokenType::RightCurlyBracket) {
+ if (tokens[i].type == TokenType::EndOfLine) {
+ lines += "\n";
+ i++;
+ continue;
+ }
+ if (tokens[i].type == TokenType::EndOfFile) {
+ throw std::runtime_error("Unexpected end of file");
+ break;
+ }
+ lines += tokens[i].lexeme + " ";
+ i++;
+ }
+
+ if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
+ THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
+ }
+ return lines;
+};
+
+}; // namespace ScriptInterpreterHelpers
+
+#endif // SCRIPTINTERPRETERHELPERS_HPP
diff --git a/src/Token.hpp b/src/Token.hpp
index b90fa92..1391349 100644
--- a/src/Token.hpp
+++ b/src/Token.hpp
@@ -1,9 +1,6 @@
#ifndef TOKEN_HPP
#define TOKEN_HPP
#include <cstdint>
-#include <iostream>
-#include <ostream>
-#include <stdexcept>
#include <string>
#include <unordered_map>
@@ -12,55 +9,116 @@
enum class TokenType : std::uint8_t {
ParserOpenTag,
ParserCloseTag,
+ ParserIfStatement, // if
FileClose,
Identifier,
StringLiteral,
IntLiteral,
DoubleLiteral,
- LeftParenthesis,
- RightParenthesis,
- Comma,
- Semicolon,
- Variable, // $variable
- VariableSign, // $ variable start sign
- StringDeclaration, // string $variable
- IntDeclaration, // int $variable
- DoubleDeclaration, // double $variable
- Equals, // = jel
- EndOfFile,
- EndOfLine,
- Comment,
- Unknown
+ BooleanLiteral,
+ LeftParenthesis, // (
+ RightParenthesis, // )
+ Comma, // ,
+ Semicolon, // ;
+ Variable, // $variable
+ VariableSign, // $ variable start sign
+ StringDeclaration, // string $variable
+ IntDeclaration, // int $variable
+ DoubleDeclaration, // double $variable
+ BooleanDeclaration, // bool $variable
+ FunctionDeclaration, // function $variable
+ Equals, // =
+ Plus, // +
+ Minus, // -
+ Multiply, // *
+ Divide, // /
+ Modulo, // %
+ GreaterThan, // >
+ LessThan, // <
+ GreaterThanOrEqual, // >=
+ LessThanOrEqual, // <=
+ NotEqual, // !=
+ Equal, // ==
+ Not, // !
+ And, // &&
+ Or, // ||
+ LeftBracket, // [
+ RightBracket, // ]
+ LeftCurlyBracket, // {
+ RightCurlyBracket, // }
+ EndOfFile, // \0
+ EndOfLine, // \n
+ Comment, // #
+ Unknown // Unknown
};
const static std::unordered_map<TokenType, std::string> tokenTypeNames = {
- { TokenType::ParserOpenTag, "ParserOpenTag" },
- { TokenType::ParserCloseTag, "ParserCloseTag" },
- { TokenType::FileClose, "FileClose" },
- { TokenType::Identifier, "Identifier" },
- { TokenType::StringLiteral, "StringLiteral" },
- { TokenType::IntLiteral, "IntLiteral" },
- { TokenType::DoubleLiteral, "DoubleLiteral" },
- { TokenType::LeftParenthesis, "LeftParenthesis" },
- { TokenType::RightParenthesis, "RightParenthesis" },
- { TokenType::Comma, "Comma" },
- { TokenType::Semicolon, "Semicolon" },
- { TokenType::Variable, "Variable" },
- { TokenType::VariableSign, "VariableSign" },
- { TokenType::StringDeclaration, "StringDeclaration" },
- { TokenType::IntDeclaration, "IntDeclaration" },
- { TokenType::DoubleDeclaration, "DoubleDeclaration" },
- { TokenType::Equals, "Equals" },
- { TokenType::EndOfFile, "EndOfFile" },
- { TokenType::EndOfLine, "EndOfLine" },
- { TokenType::Comment, "Comment" },
- { TokenType::Unknown, "Unknown" }
+ { TokenType::ParserOpenTag, "ParserOpenTag" },
+ { TokenType::ParserCloseTag, "ParserCloseTag" },
+ { TokenType::ParserIfStatement, "ParserIfStatement" },
+ { TokenType::FileClose, "FileClose" },
+ { TokenType::Identifier, "Identifier" },
+ { TokenType::StringLiteral, "StringLiteral" },
+ { TokenType::IntLiteral, "IntLiteral" },
+ { TokenType::DoubleLiteral, "DoubleLiteral" },
+ { TokenType::BooleanLiteral, "BooleanLiteral" },
+ { TokenType::LeftParenthesis, "LeftParenthesis" },
+ { TokenType::RightParenthesis, "RightParenthesis" },
+ { TokenType::Comma, "Comma" },
+ { TokenType::Semicolon, "Semicolon" },
+ { TokenType::Variable, "Variable" },
+ { TokenType::VariableSign, "VariableSign" },
+ { TokenType::StringDeclaration, "StringDeclaration" },
+ { TokenType::IntDeclaration, "IntDeclaration" },
+ { TokenType::DoubleDeclaration, "DoubleDeclaration" },
+ { TokenType::BooleanDeclaration, "BooleanDeclaration" },
+ { TokenType::FunctionDeclaration, "FunctionDeclaration" },
+ { TokenType::Equals, "Equals" },
+ { TokenType::Plus, "Plus" },
+ { TokenType::Minus, "Minus" },
+ { TokenType::Multiply, "Multiply" },
+ { TokenType::Divide, "Divide" },
+ { TokenType::Modulo, "Modulo" },
+ { TokenType::GreaterThan, "GreaterThan" },
+ { TokenType::LessThan, "LessThan" },
+ { TokenType::GreaterThanOrEqual, "GreaterThanOrEqual" },
+ { TokenType::LessThanOrEqual, "LessThanOrEqual" },
+ { TokenType::NotEqual, "NotEqual" },
+ { TokenType::Equal, "Equal" },
+ { TokenType::Not, "Not" },
+ { TokenType::And, "And" },
+ { TokenType::Or, "Or" },
+ { TokenType::LeftBracket, "LeftBracket" },
+ { TokenType::RightBracket, "RightBracket" },
+ { TokenType::LeftCurlyBracket, "LeftCurlyBracket" },
+ { TokenType::RightCurlyBracket, "RightCurlyBracket" },
+ { TokenType::EndOfFile, "EndOfFile" },
+ { TokenType::EndOfLine, "EndOfLine" },
+ { TokenType::Comment, "Comment" },
+ { TokenType::Unknown, "Unknown" }
+};
+
+[[nodiscard]] static inline std::string getTokenTypeAsString(TokenType type) {
+ auto it = tokenTypeNames.find(type);
+ if (it != tokenTypeNames.end()) {
+ return it->second;
+ }
+ return "Unknown";
+ //throw std::runtime_error("Unknown token type");
};
static const std::unordered_map<TokenType, Variables::Type> tokenTypeToVariableType = {
- { TokenType::StringLiteral, Variables::Type::VT_STRING },
- { TokenType::IntLiteral, Variables::Type::VT_INT },
- { TokenType::DoubleLiteral, Variables::Type::VT_DOUBLE }
+ { TokenType::StringLiteral, Variables::Type::VT_STRING },
+ { TokenType::IntLiteral, Variables::Type::VT_INT },
+ { TokenType::DoubleLiteral, Variables::Type::VT_DOUBLE },
+ { TokenType::BooleanLiteral, Variables::Type::VT_BOOLEAN }
+};
+
+static const std::unordered_map<Variables::Type, TokenType> variableTypeToTokenType = {
+ { Variables::Type::VT_STRING, TokenType::StringLiteral },
+ { Variables::Type::VT_INT, TokenType::IntLiteral },
+ { Variables::Type::VT_DOUBLE, TokenType::DoubleLiteral },
+ { Variables::Type::VT_BOOLEAN, TokenType::BooleanLiteral }
};
[[nodiscard]] static inline Variables::Type getVariableTypeFromTokenType(TokenType type) {
@@ -76,12 +134,64 @@
return Variables::TypeToString(getVariableTypeFromTokenType(type));
}
+[[nodiscard]] static inline TokenType getTokenTypeFromVariableType(Variables::Type type) {
+ auto it = variableTypeToTokenType.find(type);
+ if (it != variableTypeToTokenType.end()) {
+ return it->second;
+ }
+ return TokenType::Unknown;
+};
+
+[[nodiscard]] static inline TokenType getTokenTypeFromValueDeclaration(const Variables::Type & declaration) {
+ if (declaration == Variables::Type::VT_STRING) {
+ return TokenType::StringDeclaration;
+ }
+ if (declaration == Variables::Type::VT_INT) {
+ return TokenType::IntDeclaration;
+ }
+ if (declaration == Variables::Type::VT_DOUBLE) {
+ return TokenType::DoubleDeclaration;
+ }
+ if (declaration == Variables::Type::VT_BOOLEAN) {
+ return TokenType::BooleanDeclaration;
+ }
+ if (declaration == Variables::Type::VT_FUNCTION) {
+ return TokenType::FunctionDeclaration;
+ }
+ return TokenType::Unknown;
+}
+
+[[nodiscard]] static inline Variables::Type getVariableTypeFromTokenTypeDeclaration(const TokenType & type) {
+ if (type == TokenType::StringDeclaration) {
+ return Variables::Type::VT_STRING;
+ }
+ if (type == TokenType::IntDeclaration) {
+ return Variables::Type::VT_INT;
+ }
+ if (type == TokenType::DoubleDeclaration) {
+ return Variables::Type::VT_DOUBLE;
+ }
+ if (type == TokenType::BooleanDeclaration) {
+ return Variables::Type::VT_BOOLEAN;
+ }
+ if (type == TokenType::FunctionDeclaration) {
+ return Variables::Type::VT_FUNCTION;
+ }
+ return Variables::Type::VT_NULL;
+};
+
+struct TokenPos {
+ size_t start;
+ size_t end;
+};
+
struct Token {
TokenType type;
std::string lexeme;
std::string file;
int lineNumber;
size_t columnNumber;
+ TokenPos pos;
Variables::Type variableType = Variables::Type::VT_NULL;
[[nodiscard]] std::string getTypeName() const { return tokenTypeNames.at(type); }
diff --git a/src/Value.hpp b/src/Value.hpp
index 2a15758..520e0de 100644
--- a/src/Value.hpp
+++ b/src/Value.hpp
@@ -4,35 +4,77 @@
#include <string>
#include <variant>
+#include "Token.hpp"
#include "VariableTypes.hpp"
class Value {
public:
Variables::Type type = Variables::Type::VT_NULL;
Variables::DataContainer data;
+ Token token;
Value() : type(Variables::Type::VT_NULL) {}
- Value(Variables::Type t, double val) : type(t), data(std::move(val)) {}
+ //Value(Variables::Type t, double val) : type(t), data(std::move(val)) {}
- Value(Variables::Type t, int val) : type(t), data(std::move(val)) {}
+ //Value(Variables::Type t, int val) : type(t), data(std::move(val)) {}
- Value(Variables::Type t, const std::string & val) : type(t), data(val) {}
+ //Value(Variables::Type t, const std::string & val) : type(t), data(val) {}
- Value(Variables::Type t, bool val) : type(t), data(std::move(val)) {}
+ //Value(Variables::Type t, bool val) : type(t), data(std::move(val)) {}
- static Value fromInt(int val) { return Value(Variables::Type::VT_INT, val); }
+ Value(Variables::Type variable_type, const Token & token) : type(variable_type), token(token) {
+ if (type == Variables::Type::VT_INT) {
+ data = std::stoi(token.lexeme);
+ } else if (type == Variables::Type::VT_DOUBLE) {
+ data = std::stod(token.lexeme);
+ } else if (type == Variables::Type::VT_BOOLEAN) {
+ data = token.lexeme == "true";
+ } else if (type == Variables::Type::VT_NULL) {
+ data = Variables::TypeToString(type);
+ } else if (type == Variables::Type::VT_NOT_DEFINED) {
+ data = Variables::TypeToString(type);
+ } else {
+ data = token.lexeme;
+ }
+ }
- static Value fromDouble(double val) { return Value(Variables::Type::VT_DOUBLE, val); }
+ //Value(const Token & token) : token(token), type(token.variableType), data(token.lexeme) {}
- static Value fromString(const std::string & val) { return { Variables::Type::VT_STRING, val }; }
+ //static Value fromInt(int val) { return Value(Variables::Type::VT_INT, val); }
- static Value fromBoolean(bool val) { return { Variables::Type::VT_BOOLEAN, val }; }
+ //static Value fromDouble(double val) { return Value(Variables::Type::VT_DOUBLE, val); }
+
+ //static Value fromString(const std::string & val) { return { Variables::Type::VT_STRING, val }; }
+
+ //static Value fromBoolean(bool val) { return { Variables::Type::VT_BOOLEAN, val }; }
+
+ static Value fromInt(const Token & token) { return Value(Variables::Type::VT_INT, token); }
+
+ static Value fromDouble(const Token & token) { return Value(Variables::Type::VT_DOUBLE, token); }
+
+ static Value fromString(const Token & token) { return Value(Variables::Type::VT_STRING, token); }
+
+ static Value fromBoolean(const Token & token, bool state) {
+ auto result = Value(Variables::Type::VT_BOOLEAN, token);
+ result.data = state;
+ return result;
+ }
std::string ToString() const { return decodeEscapes(Variables::ToString(data, type)); }
+ int ToInt() const { return std::get<int>(data); }
+
+ double ToDouble() const { return std::get<double>(data); }
+
+ bool ToBool() const { return std::get<bool>(data); }
+
std::string TypeToString() const { return Variables::TypeToString(type); }
+ void SetToken(const Token & token) { this->token = token; }
+
+ const Token & GetToken() const { return token; }
+
private:
Value(Variables::Type t, std::variant<int, double, std::string, bool> && val) : type(t), data(std::move(val)) {}
diff --git a/src/VariableTypes.hpp b/src/VariableTypes.hpp
index 6a3d5f1..e03c242 100644
--- a/src/VariableTypes.hpp
+++ b/src/VariableTypes.hpp
@@ -10,22 +10,25 @@
using DataContainer = std::variant<int, double, std::string, bool>;
-enum class Type : std::uint8_t { VT_INT, VT_DOUBLE, VT_STRING, VT_BOOLEAN, VT_NULL, VT_NOT_DEFINED };
+enum class Type : std::uint8_t { VT_INT, VT_DOUBLE, VT_STRING, VT_BOOLEAN, VT_NULL, VT_FUNCTION, VT_NOT_DEFINED };
const std::unordered_map<std::string, Type> StringToTypeMap = {
{ "int", Type::VT_INT },
{ "double", Type::VT_DOUBLE },
{ "string", Type::VT_STRING },
+ { "bool", Type::VT_BOOLEAN },
{ "boolean", Type::VT_BOOLEAN },
{ "null", Type::VT_NULL },
+ { "function", Type::VT_FUNCTION },
{ "not_defined", Type::VT_NOT_DEFINED },
};
const std::unordered_map<Type, std::string> StypeToStringMap = {
{ Type::VT_INT, "int" },
{ Type::VT_DOUBLE, "double" },
{ Type::VT_STRING, "string" },
- { Type::VT_BOOLEAN, "boolean" },
+ { Type::VT_BOOLEAN, "bool" },
{ Type::VT_NULL, "null" },
+ { Type::VT_FUNCTION, "function" },
{ Type::VT_NOT_DEFINED, "not_defined" },
};
diff --git a/test_scripts/test1.ss b/test_scripts/test1.vs
similarity index 100%
rename from test_scripts/test1.ss
rename to test_scripts/test1.vs
diff --git a/test_scripts/test2.ss b/test_scripts/test2.vs
similarity index 100%
rename from test_scripts/test2.ss
rename to test_scripts/test2.vs
--
Gitblit v1.9.3