A simple scripting language in C++
Ferenc Szontágh
2025-04-14 c1a905b5020c4f2f4ade85577e0c36811be87de4
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 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