A simple scripting language in C++
Ferenc Szontágh
2025-04-18 7934a339e9b2319e0234e8f2ca5d952eff243c05
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#ifndef VOIDSCRIPT_HPP
#define VOIDSCRIPT_HPP
#include <filesystem>
#include <fstream>
#include <string>
 
#include "Interpreter/Interpreter.hpp"
#include "Lexer/Lexer.hpp"
#include "Modules/ModuleManager.hpp"
#include "Modules/PrintNlModule.hpp"
#include "Modules/PrintModule.hpp"
#include "Modules/TypeofModule.hpp"
#include "Modules/FileModule.hpp"
#include "Modules/JsonModule.hpp"
#include "Parser/Parser.hpp"
 
class VoidScript {
  private:
    // Debug flags for various components
    bool                            debugLexer_       = false;
    bool                            debugParser_      = false;
    bool                            debugInterpreter_ = false;
    bool                            debugSymbolTable_ = false;
    std::vector<std::string>        files;
    std::shared_ptr<Lexer::Lexer>   lexer  = nullptr;
    std::shared_ptr<Parser::Parser> parser = nullptr;
 
    static std::string readFile(const std::string & file) {
        if (!std::filesystem::exists(file)) {
            throw std::runtime_error("File " + file + " does not exits");
        }
        std::ifstream input(file, std::ios::in);
        if (!input.is_open()) {
            throw std::runtime_error("Could not open file " + file);
            return "";
        }
        std::string content = std::string((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
        input.close();
        return content;
    }
 
  public:
    /**
     * @param file               initial script file
     * @param debugLexer         enable lexer debug output
     * @param debugParser        enable parser debug output
     * @param debugInterpreter   enable interpreter debug output
     */
    VoidScript(const std::string & file, bool debugLexer = false, bool debugParser = false,
               bool debugInterpreter = false, bool debugSymbolTable = false) :
        debugLexer_(debugLexer),
        debugParser_(debugParser),
        debugInterpreter_(debugInterpreter),
        debugSymbolTable_(debugSymbolTable),
        lexer(std::make_shared<Lexer::Lexer>()),
        parser(std::make_shared<Parser::Parser>()) {
        // Register built-in modules (print, etc.)
        Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintModule>());
        Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintNlModule>());
        // typeof() builtin
        Modules::ModuleManager::instance().addModule(std::make_unique<Modules::TypeofModule>());
        // file I/O builtin
        Modules::ModuleManager::instance().addModule(std::make_unique<Modules::FileModule>());
        // JSON encode/decode builtin
        Modules::ModuleManager::instance().addModule(std::make_unique<Modules::JsonModule>());
        this->files.emplace(this->files.begin(), file);
 
        lexer->setKeyWords(Parser::Parser::keywords);
    }
 
    int run() {
        try {
            // Load plugin modules from 'modules' directory (case-insensitive)
            Modules::ModuleManager::instance().loadPlugins("modules");
            Modules::ModuleManager::instance().loadPlugins("Modules");
            // Register all built-in and plugin modules before execution
            Modules::ModuleManager::instance().registerAll();
            while (!files.empty()) {
                std::string       file         = files.back();
                const std::string file_content = readFile(file);
                files.pop_back();
 
                std::string _default_namespace_ = file;
                std::replace(_default_namespace_.begin(), _default_namespace_.end(), '.', '_');
 
                Symbols::SymbolContainer::instance()->create(_default_namespace_);
 
                const std::string ns = Symbols::SymbolContainer::instance()->currentScopeName();
 
                this->lexer->addNamespaceInput(ns, file_content);
                const auto tokens = this->lexer->tokenizeNamespace(ns);
                if (debugLexer_) {
                    std::cerr << "[Debug][Lexer] Tokens for namespace '" << ns << "':\n";
                    for (const auto & tok : tokens) {
                        std::cerr << tok.dump();
                    }
                }
 
                parser->parseScript(tokens, file_content, file);
                if (debugParser_) {
                    std::cerr << "[Debug][Parser] Operations for namespace '" << ns << "':\n";
                    for (const auto & op : Operations::Container::instance()->getAll(ns)) {
                        std::cerr << op->toString() << "\n";
                    }
                }
 
                // Execute interpreter with optional debug output
                Interpreter::Interpreter interpreter(debugInterpreter_);
                interpreter.run();
                if (debugSymbolTable_) {
                    std::cout << Symbols::SymbolContainer::dump() << "\n";
                }
            }  // while (!files.empty())
 
            return 0;
        } catch (const std::exception & e) {
            std::cerr << e.what() << '\n';
            return 1;
        }
        return 1;
    }
};  // class VoidScript
 
#endif  // VOIDSCRIPT_HPP