A couple of comments.
1) Why are you using this-> everywhere? Within the body of a class function, you don't need to use this-> to refer to a class variable.
2) I'd advise against using the same name for a function parameter and a member variable.
3) Initialise member variables when they are defined. You don't then need to initialise these in the constructor(s) unless to a different value.
4) Pass containers/class etc (eg std::string) by ref/const ref and not by value to avoid a copy (unless a copy is required)
5) Use member initialization lists to initialise member variables.
Eg. For lexer.h (partial)
1 2 3 4 5
|
private:
std::string code;
char c {};
size_t i {};
|
lexer.cpp (partial)
|
Lexer::Lexer(const std::string& code_) : code(code_), c(code[i]), tokenVector(tokenize()) {}
|
There's also much scope for code simplification. Eg
1 2 3
|
std::string Lexer::getCurrentCharAsString() {
return std::string(1, c);
}
|
1 2 3 4 5 6 7
|
std::vector<Token*> Lexer::tokenize() {
std::vector<Token*> tokvector;
for (Token* token {}; (token = getNextToken())->getType() != TOKEN_EOF; tokvector.push_back(token));
return tokvector;
}
|
1 2 3 4 5 6 7 8
|
Token* Lexer::collectIdentifier() {
std::string value;
for (; isalnum(c); advance())
value += c;
return new Token(TOKEN_IDENTIFIER, value);
}
|
There's also isspace() to test if a char is a white-space char (space, tab, newline).
For AST.
You're got an enum for AST_TYPE. You're then got different member variables for each type.
Consider having a separate class/struct for each ast type. Then use a std::variant of these types which is accessed via the std::visit pattern. You then have a tree (or vector or however you're holding these) of these variants. Then use std::visit to process each of these types.