6 files modified
2 files renamed
| | |
| | | cmake_minimum_required(VERSION 3.16) |
| | | project(sonyscript LANGUAGES CXX) |
| | | cmake_minimum_required(VERSION 3.5) |
| | | project( |
| | | voidscript |
| | | LANGUAGES CXX |
| | | VERSION 0.0.1 |
| | | ) |
| | | |
| | | set(CMAKE_CXX_STANDARD 20) |
| | | set(CMAKE_CXX_STANDARD_REQUIRED ON) |
| | |
| | | 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) |
| | |
| | | |
| | | file(GLOB_RECURSE SOURCES |
| | | src/main.cpp |
| | | src/SScriptInterpreter.cpp |
| | | src/ScriptInterpreter.cpp |
| | | src/Lexer.cpp |
| | | ) |
| | | |
| | |
| | | #include <fstream> |
| | | |
| | | #include "Builtins/PrintModule.hpp" |
| | | #include "SScriptInterpreter.hpp" |
| | | #include "ScriptInterpreter.hpp" |
| | | |
| | | static bool DEBUG = false; |
| | | |
| | | int main(int argc, char * argv[]) { |
| | | SScriptInterpreter interp; |
| | | interp.registerFunction("print", std::make_shared<PrintFunction>()); |
| | | |
| | | |
| | | if (argc < 2) { |
| | | std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>" << std::endl; |
| | | return 1; |
| | | } |
| | | if (argc > 2) { |
| | | if (std::string(argv[1]) == "-d" || std::string(argv[1]) == "--debug") { |
| | | DEBUG = true; |
| | | } else { |
| | | std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>" << std::endl; |
| | | return 1; |
| | | } |
| | | } |
| | | if (argc > 3) { |
| | | std::cerr << "Error: Too many arguments." << std::endl; |
| | | std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n"; |
| | | return 1; |
| | | } |
| | | |
| | | if (!std::filesystem::exists(argv[2])) { |
| | | std::cerr << "Error: File " << argv[2] << " does not exist." << std::endl; |
| | | 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 { |
| | | std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n"; |
| | | return 1; |
| | | } |
| | | // get the absolute path of the file |
| | | const std::string filename = std::filesystem::canonical(argv[2]).string(); |
| | | |
| | | if (!std::filesystem::exists(file)) { |
| | | std::cerr << "Error: File " << file << " does not exist.\n"; |
| | | return 1; |
| | | } |
| | | |
| | | const std::string filename = std::filesystem::canonical(file).string(); |
| | | |
| | | try { |
| | | std::ifstream file(filename); |
| | | if (!file.is_open()) { |
| | | std::cerr << "Error: Could not open file " << filename << std::endl; |
| | | std::ifstream input(filename); |
| | | if (!input.is_open()) { |
| | | std::cerr << "Error: Could not open file " << filename << "\n"; |
| | | return 1; |
| | | } |
| | | |
| | | std::string content((std::istreambuf_iterator<char>(file)), 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.executeScript(content, filename, DEBUG); |
| | | } catch (const std::exception & e) { |
| | | std::cerr << "Parser error: " << e.what() << std::endl; |
| | | std::cerr << "Parser error: " << e.what() << "\n"; |
| | | return 1; |
| | | } |
| | | |
| | |
| | | const char COMMENT_CHARACTER = '@COMMENT_CHARACTER@'; |
| | | const static char * PARSER_OPEN_TAG = "<?void"; |
| | | const static char * PARSER_CLOSE_TAG = "?>"; |
| | | #cmakedefine BUILD_TYPE @CMAKE_BUILD_TYPE @ |
| | | 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 |
| | |
| | | #include "Value.hpp" |
| | | |
| | | |
| | | class SScriptInterpreter; |
| | | class ScriptInterpreter; |
| | | |
| | | class BaseFunction { |
| | | protected: |
| | |
| | | virtual void validate(const std::vector<Token> & tokens, size_t & i) const = 0; |
| | | virtual Value call(const std::vector<Value> & args, bool debug = false) const = 0; |
| | | |
| | | template <typename FuncClass> void registerFunctionTo(SScriptInterpreter & interp) { |
| | | template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) { |
| | | FuncClass::registerTo(interp); |
| | | } |
| | | }; |
| | |
| | | |
| | | #include "BaseFunction.hpp" |
| | | #include "ScriptExceptionMacros.h" |
| | | #include "SScriptInterpreter.hpp" |
| | | #include "ScriptInterpreter.hpp" |
| | | #include "Token.hpp" |
| | | #include "Value.hpp" |
| | | |
| | |
| | | #define SCRIPT_EXCEPTION_MACROS_H |
| | | |
| | | #define THROW_UNEXPECTED_TOKEN_ERROR(token, expected) \ |
| | | SScriptInterpreter::throwUnexpectedTokenError(token, expected, __FILE__, __LINE__) |
| | | ScriptInterpreter::throwUnexpectedTokenError(token, expected, __FILE__, __LINE__) |
| | | |
| | | #define THROW_UNDEFINED_VARIABLE_ERROR(name, token) \ |
| | | SScriptInterpreter::throwUndefinedVariableError(name, token, __FILE__, __LINE__) |
| | | ScriptInterpreter::throwUndefinedVariableError(name, token, __FILE__, __LINE__) |
| | | |
| | | #define THROW_VARIABLE_TYPE_MISSMATCH_ERROR(target_variable_name, target_variable_type, source_variable_name, \ |
| | | source_variable_type, token) \ |
| | | SScriptInterpreter::throwVariableTypeMissmatchError(target_variable_name, target_variable_type, \ |
| | | ScriptInterpreter::throwVariableTypeMissmatchError(target_variable_name, target_variable_type, \ |
| | | source_variable_name, source_variable_type, token, __FILE__, \ |
| | | __LINE__) |
| | | |
| | | #define THROW_VARIABLE_REDEFINITION_ERROR(name, token) \ |
| | | SScriptInterpreter::throwVariableRedefinitionError(name, token, __FILE__, __LINE__) |
| | | ScriptInterpreter::throwVariableRedefinitionError(name, token, __FILE__, __LINE__) |
| | | |
| | | #endif // SCRIPT_EXCEPTION_MACROS_H |
| File was renamed from src/SScriptInterpreter.cpp |
| | |
| | | #include "SScriptInterpreter.hpp" |
| | | #include "ScriptInterpreter.hpp" |
| | | |
| | | #include <iostream> |
| | | #include <stdexcept> |
| | |
| | | #include "ScriptExceptionMacros.h" |
| | | #include "Value.hpp" |
| | | |
| | | void SScriptInterpreter::registerFunction(const std::string & name, std::shared_ptr<BaseFunction> fn) { |
| | | void ScriptInterpreter::registerFunction(const std::string & name, std::shared_ptr<BaseFunction> fn) { |
| | | functionObjects[name] = std::move(fn); |
| | | } |
| | | |
| | | Value SScriptInterpreter::evaluateExpression(const Token & token) const { |
| | | Value ScriptInterpreter::evaluateExpression(const Token & token) const { |
| | | if (token.type == TokenType::StringLiteral) { |
| | | return Value::fromString(token.lexeme); |
| | | } |
| | |
| | | return Value(); |
| | | } |
| | | |
| | | std::vector<Value> SScriptInterpreter::parseArguments(const std::vector<Token> & tokens, |
| | | std::vector<Value> ScriptInterpreter::parseArguments(const std::vector<Token> & tokens, |
| | | std::size_t & current_index) const { |
| | | std::vector<Value> args; |
| | | |
| | |
| | | return args; |
| | | } |
| | | |
| | | void SScriptInterpreter::handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i) { |
| | | void ScriptInterpreter::handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i) { |
| | | const auto varName = tokens[i].lexeme; |
| | | const auto varType = tokens[i].variableType; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | void SScriptInterpreter::handleNumberDeclaration(const std::vector<Token> & tokens, std::size_t & i, TokenType type) { |
| | | 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; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | void SScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) { |
| | | void ScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) { |
| | | std::string funcName = tokens[i].lexeme; |
| | | auto it = functionObjects.find(funcName); |
| | | if (it == functionObjects.end()) { |
| | |
| | | } |
| | | } |
| | | |
| | | void SScriptInterpreter::handleVariableReference(const std::vector<Token> & tokens, std::size_t & i) { |
| | | 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; |
| | |
| | | } |
| | | } |
| | | |
| | | void SScriptInterpreter::handleComment(std::size_t & i) { |
| | | void ScriptInterpreter::handleComment(std::size_t & i) { |
| | | i++; // Skip comment token |
| | | } |
| | | |
| | | void SScriptInterpreter::handleSemicolon(std::size_t & i) { |
| | | void ScriptInterpreter::handleSemicolon(std::size_t & i) { |
| | | i++; // Skip semicolon token |
| | | } |
| | | |
| | | void SScriptInterpreter::expectSemicolon(const std::vector<Token> & tokens, std::size_t & i, |
| | | 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); |
| | |
| | | } |
| | | } |
| | | |
| | | void SScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool debug) { |
| | | void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool debug) { |
| | | Lexer lexer(source, filename); |
| | | auto tokens = lexer.tokenize(); |
| | | |
| File was renamed from src/SScriptInterpreter.hpp |
| | |
| | | |
| | | using FunctionValidator = std::function<void(const std::vector<Token> &, size_t &)>; |
| | | |
| | | class SScriptInterpreter { |
| | | 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); |
| | |
| | | "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); |
| | | #if BUILD_TYPE == Debug |
| | | #ifdef DEBUG_BUILD |
| | | const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content; |
| | | #else |
| | | const std::string error_message = error_content; |
| | | const std::string& error_message = error_content; |
| | | #endif |
| | | throw std::runtime_error(error_message); |
| | | }; |
| | |
| | | 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); |
| | | #if BUILD_TYPE == Debug |
| | | #ifdef DEBUG_BUILD |
| | | const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content; |
| | | #else |
| | | const std::string error_message = error_content; |
| | | 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) { |
| | | 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 += " in file: " + token.file + ":" + std::to_string(token.lineNumber) + ":" + |
| | | std::to_string(token.columnNumber); |
| | | #if BUILD_TYPE == Debug |
| | | #ifdef DEBUG_BUILD |
| | | const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content; |
| | | #else |
| | | const std::string error_message = error_content; |
| | |
| | | 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); |
| | | #if BUILD_TYPE == Debug |
| | | #ifdef DEBUG_BUILD |
| | | const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content; |
| | | #else |
| | | const std::string error_message = error_content; |
| | | const std::string& error_message = error_content; |
| | | #endif |
| | | throw std::runtime_error(error_message); |
| | | } |