A simple scripting language in C++
Ferenc Szontágh
2025-04-13 cb3065c34756a70cb6006fc25777ce3e720ff1a8
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
#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, const size_t & start, const 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 &&
        tokens[i].type != TokenType::RightParenthesis) {
        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "variable declaration", file, line);
    }
    if (tokens[i].type != TokenType::RightParenthesis) {
        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;
}
 
static void getFunctionBody(const std::vector<Token> & tokens, std::size_t & i, std::size_t & start,
                            std::size_t & end) {
    start = tokens[i].pos.end;
    std::cout << "START Token: " << tokens[i].lexeme << " start pos: " << std::to_string(tokens[i].pos.start)
              << " end pos: " << std::to_string(tokens[i].pos.end) << std::endl;
 
    if (i >= tokens.size() || tokens[i].type != TokenType::LeftCurlyBracket) {
        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "{");
    }
    i++;  // Skip '{'
 
    while (i < tokens.size() && tokens[i].type != TokenType::RightCurlyBracket) {
        if (tokens[i].type == TokenType::EndOfLine) {
            i++;
            continue;
        }
        if (tokens[i].type == TokenType::EndOfFile) {
            throw std::runtime_error("Unexpected end of file");
            break;
        }
        i++;
    }
    end = tokens[i].pos.start - 1;
 
    std::cout << "END Token: " << tokens[i].lexeme << " start pos: " << std::to_string(tokens[i].pos.start)
              << " end pos: " << std::to_string(tokens[i].pos.end) << std::endl;
 
    if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
    }
};
 
};  // namespace ScriptInterpreterHelpers
 
#endif  // SCRIPTINTERPRETERHELPERS_HPP