A simple scripting language in C++
Ferenc Szontágh
2025-04-18 c91e935c62b8e254b9daadf37b915c983518bff4
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
125
126
127
128
129
130
#ifndef PARSEDEXPRESSION_HPP
#define PARSEDEXPRESSION_HPP
 
#include <memory>
#include <string>
#include <vector>
 
#include "../Symbols/SymbolContainer.hpp"
#include "../Symbols/Value.hpp"
#include "../Symbols/FunctionSymbol.hpp"
 
namespace Parser {
 
struct ParsedExpression;
 
using ParsedExpressionPtr = std::unique_ptr<ParsedExpression>;
 
struct ParsedExpression {
    enum class Kind : std::uint8_t { Literal, Variable, Binary, Unary, Call };
 
    Kind kind;
 
    Symbols::Value value;
    std::string    name;
 
    // For operations
    std::string         op;
    ParsedExpressionPtr lhs;
    ParsedExpressionPtr rhs;
    // For function call arguments
    std::vector<ParsedExpressionPtr> args;
 
    // Constructor for literal
    static ParsedExpressionPtr makeLiteral(const Symbols::Value & val) {
        auto expr   = std::make_unique<ParsedExpression>();
        expr->kind  = Kind::Literal;
        expr->value = val;
        return expr;
    }
 
    // Constructor for variable
    static ParsedExpressionPtr makeVariable(const std::string & name) {
        auto expr  = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Variable;
        expr->name = name;
        return expr;
    }
 
    // Constructor for binary operation
    static ParsedExpressionPtr makeBinary(std::string op, ParsedExpressionPtr left, ParsedExpressionPtr right) {
        auto expr  = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Binary;
        expr->op   = std::move(op);
        expr->lhs  = std::move(left);
        expr->rhs  = std::move(right);
        return expr;
    }
 
    // Constructor for unary operation
    static ParsedExpressionPtr makeUnary(std::string op, ParsedExpressionPtr operand) {
        auto expr  = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Unary;
        expr->op   = std::move(op);
        expr->rhs  = std::move(operand);
        return expr;
    }
    // Constructor for function call
    static ParsedExpressionPtr makeCall(const std::string &name, std::vector<ParsedExpressionPtr> arguments) {
        auto expr        = std::make_unique<ParsedExpression>();
        expr->kind       = Kind::Call;
        expr->name       = name;
        expr->args       = std::move(arguments);
        return expr;
    }
 
    Symbols::Variables::Type getType() const {
        switch (kind) {
            case Kind::Literal:
                return value.getType();
                break;
 
            case Kind::Variable:
                {
                    const auto ns     = Symbols::SymbolContainer::instance()->currentScopeName() + ".variables";
                    auto       symbol = Symbols::SymbolContainer::instance()->get(ns, name);
                    if (!symbol) {
                        throw std::runtime_error("Unknown variable: " + name + " in namespace: " + ns +
                                                 " File: " + __FILE__ + ":" + std::to_string(__LINE__));
                    }
                    return symbol->getValue().getType();
                }
 
            case Kind::Binary:
                {
                    auto lhsType = lhs->value.getType();
                    //auto rhsType = rhs->value.getType();
                    return lhsType;  // In binary expressions, operand types match, so we can return the left-hand type
                }
 
            case Kind::Unary:
                {
                    //auto operandType = op.
                    if (op == "!") {
                        return Symbols::Variables::Type::BOOLEAN;  // Because the '!' operator expects a boolean type
                    }
                    break;
                }
            case Kind::Call:
                {
                    const std::string ns = Symbols::SymbolContainer::instance()->currentScopeName() + ".functions";
                    auto symbol = Symbols::SymbolContainer::instance()->get(ns, name);
                    if (!symbol) {
                        throw std::runtime_error("Unknown function: " + name + " in namespace: " + ns);
                    }
                    // FunctionSymbol holds return type
                    auto funcSym = std::static_pointer_cast<Symbols::FunctionSymbol>(symbol);
                    return funcSym->returnType();
                }
 
            default:
                throw std::runtime_error("Unknown expression kind");
        }
 
        throw std::runtime_error("Could not determine type for expression");
    }
};
 
}  // namespace Parser
 
#endif  // PARSEDEXPRESSION_HPP