...
Current stack (top to bottom):
  Node: main, State: 3
  Node: BOF BOF, State: 1
-----------------------------
Current stack (top to bottom):
  Node: procedures, State: 4
  Node: BOF BOF, State: 1
-----------------------------
Current stack (top to bottom):
  Node: EOF EOF, State: 7
  Node: procedures, State: 4
  Node: BOF BOF, State: 1
-----------------------------
ERROR at 16
Full parse tree at the point of error:
BOF BOF

it's not acknowledging the start BOF procedures EOF from the 7 0 .ACCEPT reduction. how do i ensure this? i think add a check to see if it's in the accept state, if so then use the reduction rule that it provides. so like we are at state 7 at the end at the top of our stack and that is accepting, so then go to the rule and reduce our stack like normal. pop off however many n times there are and then replace with the initial operator, so BOF procedures EOF becomes start; then print left to right pre-order traversal with this knowledge

7 0 .ACCEPT

#include <iostream>
#include <sstream>
#include <vector>
#include <deque>
#include <map>
#include <set>
#include "wlp4data.h" // Include the provided header file

using namespace std;

void printErr(int errorPointer) {
    cerr << "ERROR at " << errorPointer + 1 << endl;
}

struct Node {
    string value;
    vector<Node*> children;

    Node(const string &val) : value(val) {}
    void printPreOrder() {
        cout << value << endl;
        for (Node* child : children) {
            child->printPreOrder();
        }
    }
};

void printStack(const vector<pair<Node*, int>>& stack) {
    cout << "Current stack (top to bottom):" << endl;
    for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
        cout << "  Node: " << it->first->value << ", State: " << it->second << endl;
    }
    cout << "-----------------------------" << endl;
}

void printTokens(const deque<pair<string, string>>& tokens) {
    cout << "Tokens:" << endl;
    for (const auto& token : tokens) {
        cout << "  Kind: " << token.first << ", Lexeme: " << token.second << endl;
    }
}

int main() {
    // Load WLP4 parsing data from wlp4data.h
    istringstream input(WLP4_COMBINED);
    string line;

    vector<pair<string, vector<string>>> cfg;
    map<pair<int, string>, int> transitions;
    map<pair<int, string>, int> reductions;
    set<int> accept;

    // Parse the CFG
    while (getline(input, line)) {
        if (line == ".CFG") continue;
        if (line == ".TRANSITIONS") break;
        
        istringstream iss(line);
        string cfgStart, symbol;
        vector<string> cfgResult;

        iss >> cfgStart;
        while (iss >> symbol) {
            if (symbol == ".EMPTY") {
                cfgResult.clear();
                break;
            }
            cfgResult.push_back(symbol);
        }
        cfg.push_back({cfgStart, cfgResult});
    }

    // Parse the transitions
    while (getline(input, line)) {
        if (line == ".REDUCTIONS") break;

        istringstream iss(line);
        int fromState, toState;
        string symbol;

        iss >> fromState >> symbol >> toState;
        transitions[{fromState, symbol}] = toState;
    }

    // Parse the reductions
    while (getline(input, line)) {
        if (line == ".END") break;

        istringstream iss(line);
        int state, ruleNumber;
        string tag;

        iss >> state >> ruleNumber >> tag;
        if (tag == ".ACCEPT") {
            accept.insert(state);
        } else {
            reductions[{state, tag}] = ruleNumber;
        }
    }

    // Read input tokens
    deque<pair<string, string>> tokens;
    while (getline(cin, line)) {
        istringstream iss(line);
        string kind, lexeme;
        iss >> kind >> lexeme;
        tokens.push_back({kind, lexeme});
    }

    // Add BOF and EOF tokens
    tokens.push_front({"BOF", "BOF"});
    tokens.push_back({"EOF", "EOF"});

    // Print tokens for debugging
    printTokens(tokens);

    // Initialize parse tree and stack
    vector<pair<Node*, int>> stack;
    int curr = 0;
    int errorPointer = 0;

    // Handle BOF
    if (transitions.find({0, "BOF"}) != transitions.end()) {
        Node* bofNode = new Node("BOF BOF");
        stack.push_back({bofNode, transitions[{0, "BOF"}]});
        curr++;
    } else {
        printErr(0);
        return 0;
    }

    // Main parsing loop
    while (true) {
        printStack(stack);  // Print the stack at each stage

        int currentState = stack.back().second;
        string nextKind = (curr < tokens.size()) ? tokens[curr].first : "";

        // Check if we are in an accepting state and the next token is EOF
        if (currentState == 7 && nextKind == "EOF") {
            // Apply the final reduction: start -> BOF procedures EOF
            Node* eofNode = new Node("EOF EOF");
            Node* proceduresNode = stack.back().first;
            stack.pop_back();
            Node* bofNode = stack.back().first;
            stack.pop_back();

            Node* startNode = new Node("start");
            startNode->children.push_back(bofNode);
            startNode->children.push_back(proceduresNode);
            startNode->children.push_back(eofNode);

            startNode->printPreOrder();  // Print the complete parse tree
            break;
        }

        if (reductions.find({currentState, nextKind}) != reductions.end()) {
            int ruleNumber = reductions[{currentState, nextKind}];
            if (ruleNumber >= cfg.size()) {
                printErr(errorPointer);
                return 0;
            }

            auto rule = cfg[ruleNumber];
            Node* newNode = new Node(rule.first);
            for (size_t i = 0; i < rule.second.size(); ++i) {
                if (stack.empty()) {
                    printErr(errorPointer);
                    return 0;
                }
                newNode->children.insert(newNode->children.begin(), stack.back().first);
                stack.pop_back();
            }

            int topState = stack.back().second;
            if (transitions.find({topState, rule.first}) != transitions.end()) {
                stack.push_back({newNode, transitions[{topState, rule.first}]});
            } else {
                printErr(errorPointer);
                return 0;
            }

        } else if (transitions.find({currentState, nextKind}) != transitions.end()) {
            Node* newNode = new Node(nextKind + " " + tokens[curr].second);
            stack.push_back({newNode, transitions[{currentState, nextKind}]});
            curr++;
            errorPointer++;
        } else {
            // If an error occurs, print the complete parse tree structure
            printErr(errorPointer);
            cout << "Full parse tree at the point of error:" << endl;
            if (!stack.empty() && stack.front().first) {
                stack.front().first->printPreOrder();
            }
            return 0;
        }
    }

    return 0;
}