A simple scripting language in C++
Ferenc Szontágh
2025-04-19 48d9278f0b75098e83e58c589ea86d006358604d
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
131
132
133
134
135
136
137
138
139
140
#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, Object };
 
    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;
    std::vector<std::pair<std::string, ParsedExpressionPtr>> objectMembers;
 
    // 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;
    }
    // Constructor for object literal
    static ParsedExpressionPtr makeObject(std::vector<std::pair<std::string, ParsedExpressionPtr>> members) {
        auto expr = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Object;
        expr->objectMembers = std::move(members);
        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();
                }
            case Kind::Object:
                return Symbols::Variables::Type::OBJECT;
 
            default:
                throw std::runtime_error("Unknown expression kind");
        }
 
        throw std::runtime_error("Could not determine type for expression");
    }
};
 
}  // namespace Parser
 
#endif  // PARSEDEXPRESSION_HPP