I am writing a handwritten recursive descent parser as a self exercise. I'd like to see if an iterative approach is possible. Generally speaking, I'd like to know what mindset I should have in order to transform interdependent recursive functions into an iterative solution.
In my current minimal example, I have a list of Tokens (which is simply a type with a lexeme) and they are consumed via recursive descent to build an abstract syntax tree represented by a unique_ptr
to an expression:
#include <string>
#include <memory>
#include <vector>
enum Type { Num, Add, Mul, LParen, RParen };
struct Token {
Type type;
std::string lexeme;
};
struct Expr {
Token token;
};
using AST = std::unique_ptr<Expr>;
struct Literal : public Expr {
double value;
};
struct Grouping : public Expr {
AST inner;
};
struct Binary : public Expr {
AST lhs, rhs;
};
using CIT = std::vector<Token>::const_iterator;
auto expression(CIT& it, CIT end) -> AST;
auto literal(CIT &it, CIT end) -> AST {
if (it != end and it->type == Type::Num) {
auto value = std::stod(it->lexeme);
auto token = *it++;
return std::make_unique<Literal>(Literal{ token, value });
}
else if (it != end and it->type == Type::LParen) {
auto token = *it++;
auto ast = std::make_unique<Grouping>(Grouping{ token, expression(it, end) });;
if (it != end and it->type == Type::RParen)
return ast;
else
throw "Mismatched parenthesis";
}
throw "Unable to parse literal";
}
auto multiplication(CIT &it, CIT end) -> AST {
auto ast = literal(it, end);
while (it != end and it->type == Type::Mul) {
auto token = *it++;
ast = std::make_unique<Binary>(Binary{ token, std::move(ast), literal(it, end) });
}
return ast;
}
auto addition(CIT &it, CIT end) -> AST {
auto ast = multiplication(it, end);
while (it != end and it->type == Type::Add) {
auto token = *it++;
ast = std::make_unique<Binary>(Binary{ token, std::move(ast), multiplication(it, end) });
}
return ast;
}
auto expression(CIT &it, CIT end) -> AST {
return addition(it, end);
}
int main() {
std::vector<Token> tokens = {
{ Type::Num, "5"},
{ Type::Add, "+"},
{ Type::LParen, "("},
{ Type::Num, "4"},
{ Type::Mul, "*"},
{ Type::Num, "3"},
{ Type::RParen, ")"},
};
auto it = tokens.begin();
auto ast = expression(it, tokens.end());
}
Here there is a circular dependency of recursive calls: addition
depends on multiplication
, multiplication
depends on literal
, and literal
'depends on' addition
.
I'd like to see if there is a way to flatten these calls into a singular iterative call. My first thoughts were to loop through the tokens and do a switch case between the precedence of operators; however, I am unsure where to go come there.
Non-complete attempt:
auto parse(const std::vector<Token>& tokens) -> AST {
AST current;
enum class Precedent {
Addition, Multiplication, Literal
};
for (const auto& token : tokens) {
switch (precedent) {
case Precedent::Addition: {
???
} break;
case Precedent::Multiplication: {
???
} break;
case Precedent::Literal: {
???
} break;
}
}
return current;
}
I feel that I am missing some kind of stack as a lot of iterative solutions from recursive solutions appear to maintain a manual call-like stack.
Any hints would be appreciated.
Edit: I've reviewed the post referring to the duplicate, though I believe my question is different that the one linked. I am not trying to make a single recursive function into an iterative one, I am trying to make multiple recursive functions into a single iterative function. I hope that explains why I asked this question.