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
125
126
127
128
129
130
131
132
133
134
135
136
137
#ifndef PARSER_HPP
#define PARSER_HPP
 
#include <stdexcept>
#include <string>
#include <vector>
 
#include "BaseException.hpp"
#include "Interpreter/StatementNode.hpp"
#include "Lexer/Token.hpp"
#include "Lexer/TokenType.hpp"
#include "Parser/ParsedExpression.hpp"
#include "Symbols/ParameterContainer.hpp"
#include "Symbols/Value.hpp"
 
namespace Parser {
 
class Parser {
  public:
    Parser() {}
 
    class Exception : public BaseException {
      public:
        using BaseException::BaseException;
        // Filename for error reporting
        static std::string current_filename_;
 
        Exception(const std::string & msg, const std::string & expected, const Lexer::Tokens::Token & token) {
            rawMessage_ = msg + ": " + token.dump();
            context_ = " in file \"" + current_filename_ + "\" at line: " + std::to_string(token.line_number)
                       + ", column: " + std::to_string(token.column_number);
            if (expected.empty() == false) {
                rawMessage_ += " (expected: " + expected + ")";
            }
            formattedMessage_ = formatMessage();
        }
 
        Exception(const std::string & msg, const std::string & expected, int line, int col) {
            rawMessage_ = msg;
            if (expected.empty() == false) {
                rawMessage_ += " (expected: " + expected + ")";
            }
            context_ = " in file \"" + current_filename_ + "\" at line: " + std::to_string(line)
                       + ", column: " + std::to_string(col);
            formattedMessage_ = formatMessage();
        }
 
        std::string formatMessage() const override { return "[Syntax ERROR] >>" + context_ + " << : " + rawMessage_; }
    };
 
    void parseScript(const std::vector<Lexer::Tokens::Token> & tokens, std::string_view input_string,
                     const std::string & filename);
    static const std::unordered_map<std::string, Lexer::Tokens::Type>              keywords;
    static const std::unordered_map<Lexer::Tokens::Type, Symbols::Variables::Type> variable_types;
 
  private:
    std::vector<Lexer::Tokens::Token> tokens_;
    std::string_view                  input_str_view_;
    size_t                            current_token_index_;
    std::string                       current_filename_;
 
    // Token stream handling and error-reporting helper functions (unchanged)
    const Lexer::Tokens::Token & currentToken() const;
 
    // Look ahead in the token stream
    const Lexer::Tokens::Token & peekToken(size_t offset = 1) const;
 
    // Consume (advance past) the current token and return it
    Lexer::Tokens::Token consumeToken();
 
    // Check if current token type matches the expected type
    // If so, consume it and return true; otherwise return false
    bool match(Lexer::Tokens::Type expected_type);
 
    // Check if current token type and value match the expected ones
    // Only use value checking for operators and punctuation
    bool match(Lexer::Tokens::Type expected_type, const std::string & expected_value);
 
    Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type);
 
    // Like expect, but also checks the token's value
    Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type, const std::string & expected_value);
 
    // Check if we've reached the end of relevant tokens (just before EOF)
    bool isAtEnd() const;
 
    [[noreturn]] void reportError(const std::string & message, const std::string & expected = "") {
        if (current_token_index_ < tokens_.size()) {
            throw Exception(message, expected, tokens_[current_token_index_]);
        }
        int line = tokens_.empty() ? 0 : tokens_.back().line_number;
        int col  = tokens_.empty() ? 0 : tokens_.back().column_number;
        throw Exception(message, expected, line, col);
    }
 
    [[noreturn]] static void reportError(const std::string & message, const Lexer::Tokens::Token & token,
                                         const std::string & expected = "") {
        throw Exception(message, expected, token);
    }
 
    // parseStatement (updated to handle return)
    void                                        parseStatement();
    void                                        parseVariableDefinition();
    void                                        parseFunctionDefinition();
    // Parse a top-level function call statement (e.g., foo(arg1, arg2);)
    void                                        parseCallStatement();
    // Parse a top-level assignment statement (variable or object member)
    void                                        parseAssignmentStatement();
    // Parse a return statement (e.g., return; or return expr;)
    void                                        parseReturnStatement();
    // Parse an if-else conditional statement (at top-level)
    void                                        parseIfStatement();
    // Parse a for-in loop over object members (at top-level)
    void                                        parseForStatement();
    // Parse an if-else conditional block and return a StatementNode (for nested blocks)
    std::unique_ptr<Interpreter::StatementNode> parseIfStatementNode();
    // Parse a for-in loop over object members and return a StatementNode (for nested blocks)
    std::unique_ptr<Interpreter::StatementNode> parseForStatementNode();
    // Parse a statement node for use inside blocks (not added to operation container)
    std::unique_ptr<Interpreter::StatementNode> parseStatementNode();
 
    // --- Parsing helper functions ---
 
    // type : KEYWORD_STRING | KEYWORD_INT | KEYWORD_DOUBLE
    // Returns the corresponding Symbols::Variables::Type enum and consumes the token
    Symbols::Variables::Type parseType();
    Symbols::Value           parseValue(Symbols::Variables::Type expected_var_type);
    Symbols::Value      parseNumericLiteral(const std::string & value, bool is_negative, Symbols::Variables::Type type);
    void                parseFunctionBody(const Lexer::Tokens::Token & opening_brace, const std::string & function_name,
                                          Symbols::Variables::Type return_type, const Symbols::FunctionParameterInfo & params);
    ParsedExpressionPtr parseParsedExpression(const Symbols::Variables::Type & expected_var_type);
 
};  // class Parser
 
}  // namespace Parser
 
#endif  // PARSER_HPP