<repo_name>floatflower/FFParser<file_sep>/src/tablerecord.h
#ifndef TABLERECORD_H
#define TABLERECORD_H

#include <QObject>
#include <QVector>

#include "rule.h"

class TableRecord : public QVector<Rule*>
{
public:
    explicit TableRecord();
    void findFirstSet();
    QVector<QString> firstSet();
    void findFollowSet();
    QVector<QString> followSet();
    void mergeFirstSet(QVector<QString> firstSet);
    void mergeFollowSet(QVector<QString> followSet);
    bool derivedLamda();
    void setKey(QString key) { m_key = key; }
    QString key() { return m_key; }
    void setHasFollowSet(bool set) { m_hasFollowSet = set; }
    bool hasFollowSet() { return m_hasFollowSet; }
signals:

public slots:
private:
    bool m_hasFirset;
    QVector<QString> m_firstSet;
    QString m_key;
    bool m_hasFollowSet;
    QVector<QString> m_followSet;
};

#endif // TABLERECORD_H
<file_sep>/src/rule.cpp
#include "rule.h"
#include "table.h"

#include <QDebug>

Rule::Rule(QObject *parent)
    : QObject(parent)
    , m_derivedLamda(true)
    , m_hasFirstSet(false)
{

}

void Rule::findFirstSet()
{
    if (m_hasFirstSet) return;
    m_hasFirstSet = true;
    Table *table = Table::instance();
    for (QVector<QString>::iterator it_derived = m_derived.begin();
         it_derived != m_derived.end();
         it_derived ++) {
        if (table->isNonterminal(*it_derived)) {

            QVector<QString> firstSet = table->firstSet(*it_derived);
            mergeFirstSet(firstSet);
            m_derivedLamda &= table->derivedLamda(*it_derived);
        }
        else {
            if (*it_derived != "lamda") {
                m_firstSet.push_back(*it_derived);
                m_derivedLamda = false;
            }
            else {
                m_derivedLamda = true;
            }
            break;
        }
        if (table->derivedLamda(*it_derived)) {

            continue;
        }
        else {
            break;
        }
    }
}

QVector<QString> Rule::firstSet()
{
    findFirstSet();
    return m_firstSet;
}

void Rule::findPredictSet()
{
    for (QVector<QString>::iterator it_firstSet = m_firstSet.begin();
         it_firstSet != m_firstSet.end();
         it_firstSet ++) {
        m_predictSet.push_back(*it_firstSet);
    }
    if (m_derivedLamda) {
        for (QVector<QString>::iterator it_followSet = m_followSet.begin();
             it_followSet != m_followSet.end();
             it_followSet ++) {
            m_predictSet.push_back(*it_followSet);
        }
    }
}

QVector<QString> Rule::predictSet()
{
    return m_predictSet;
}

void Rule::mergeFirstSet(QVector<QString>& firstSet)
{
    for (QVector<QString>::iterator it_firstSet = firstSet.begin();
         it_firstSet != firstSet.end();
         it_firstSet ++) {
        if (m_firstSet.indexOf(*it_firstSet) == -1) {
            m_firstSet.push_back(*it_firstSet);
        }
    }
}

void Rule::mergeFollowSet(QVector<QString> followSet)
{
    for (QVector<QString>::iterator it_followSet = followSet.begin();
         it_followSet != followSet.end();
         it_followSet ++) {
        if (m_followSet.indexOf(*it_followSet) == -1 && (*it_followSet) != "lamda") {
            m_followSet.push_back(*it_followSet);
        }
    }
}
<file_sep>/src/main.cpp
#include <QCoreApplication>

#include "ffparser.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    FFParser p;
    p.run();

    return 0;
}
<file_sep>/README.md
FFParser
===
[![Build Status](https://travis-ci.org/floatflower/FFParser.svg?branch=master)](https://travis-ci.org/floatflower/FFParser)

這個專案是為了編譯原理所開發的 LL(1) Parser，這個專案已經完成開發並完全符合老師要求，取得專題滿分。

This LL(1) Parser is the final project for the class "compiler", this project had been developed completely, and matched all requirement from professor.

## Notice
若需要使用此專案作為個人作業繳交，請先來信告知作者，
此外，若為其他教學用途，敬請使用並附上原始碼出處。

## Developer
+ Developer: FloatFlower.Huang
+ Email: <EMAIL>
+ Website: https://blog.floatflower.me

## How to use
### Compile this project
```
$ sudo add-apt-repository ppa:beineri/opt-qt562-trusty
$ sudo apt-get update
$ sudo apt-get install -qq qt5-qmake qt56base
$ source /opt/qt56/bin/qt56-env.sh
$ /opt/qt56/bin/qmake
$ make
```

### Execute
```
$ ./ffparser    --cfg,-c <CFG_FILE>
                --source,-s <SOURCE_CODE_PATH>
                --output,-o <OUTPUT_FILE_PATH>
```
<file_sep>/src/lookaheadtable.h
#ifndef LOOKAHEADTABLE_H
#define LOOKAHEADTABLE_H

#include <QObject>
#include <QHash>

class LookAheadTable : public QObject
{
    Q_OBJECT
public:
    explicit LookAheadTable(QObject *parent = nullptr);
    void addRule(QString nonterminal, QString terminal, int ruleNumber);
    QHash<QString, QHash<QString, int>> table() { return m_lookAheadTable; }
    int findRule(QString source, QString target);
signals:

public slots:
private:
    QHash<QString, QHash<QString, int>> m_lookAheadTable;
};

#endif // LOOKAHEADTABLE_H
<file_sep>/src/rule.h
#ifndef RULE_H
#define RULE_H

#include <QObject>
#include <QString>
#include <QVector>

class Rule : public QObject
{
    Q_OBJECT
public:
    explicit Rule(QObject *parent = nullptr);
    void setRuleNumber(int number) { m_ruleNumber = number; }
    int ruleNumber() { return m_ruleNumber; }
    void addDerived(QString derived) { m_derived.push_back(derived); }
    QVector<QString> derived() { return m_derived; }
    bool derivedLamda() { return m_derivedLamda; }
    void findFirstSet();
    QVector<QString> firstSet();
    QVector<QString> followSet() { return m_followSet; }
    void findPredictSet();
    QVector<QString> predictSet();
    void mergeFirstSet(QVector<QString> &firstSet);
    void mergeFollowSet(QVector<QString> followSet);
signals:

public slots:
private:
    int m_ruleNumber;
    QVector<QString> m_derived;
    QVector<QString> m_firstSet;
    QVector<QString> m_followSet;
    QVector<QString> m_predictSet;
    bool m_derivedLamda;
    bool m_hasFirstSet;
};

#endif // RULE_H
<file_sep>/src/tablerecord.cpp
#include "tablerecord.h"
#include <QDebug>
#include "table.h"

TableRecord::TableRecord()
    : m_hasFirset(false)
    , m_hasFollowSet(false)
{

}

void TableRecord::findFirstSet()
{
    int tableSize = size();
    for (int i = 0; i < tableSize; i ++) {
        mergeFirstSet(at(i)->firstSet());
    }
}

QVector<QString> TableRecord::firstSet()
{
    if (!m_hasFirset) findFirstSet();
    m_hasFirset = true;
    return m_firstSet;
}

void TableRecord::findFollowSet()
{
    //qDebug() << "Find Follow" << key();

    Table *table = Table::instance();
    //qDebug() << "Current finding is:" << table->currentFindingFollow();
    for (TableRecord::iterator it_tableRecord = begin();
         it_tableRecord != end();
         it_tableRecord ++) {
        // iterate all rule.

        /**
         * Iterator whole table to find who derive the "key()"
         */
        //if (!(*it_tableRecord)->derivedLamda()) continue;

        for (Table::iterator it_searchTable = table->begin();
             it_searchTable != table->end();
             it_searchTable ++) {
            //qDebug() << "Searching Record..." << it_searchTable.key();
            for (TableRecord::iterator it_searchTableRecord = (*it_searchTable)->begin();
                 it_searchTableRecord != (*it_searchTable)->end();
                 it_searchTableRecord ++) {
                //qDebug() << "Searching Rule..." << (*it_searchTableRecord)->ruleNumber();
                QVector<QString> derived = (*it_searchTableRecord)->derived();
                int derivedSize = derived.size();
                int derivedAmount = derived.count(key());

                bool tmp_derivedLamda = false;

                if (derivedAmount != 0) {
                    int thisDerived = 0;
                    for (int eachDerived = 1; eachDerived <= derivedAmount; eachDerived ++) {
                        int derivedIndex = derived.indexOf(key(), thisDerived);
                        thisDerived = derivedIndex + 1;
                        for (int i = derivedIndex + 1; i < derivedSize; i ++) {
                            //qDebug() << "Find first " << derived.at(i);
                            if (table->isNonterminal(derived.at(i))) {
                                QVector<QString> tmp_firstSet = table->firstSet(derived.at(i));

                                (*it_tableRecord)->mergeFollowSet(tmp_firstSet);

                                tmp_derivedLamda = table->derivedLamda(derived.at(i));

                            }
                            else {
                                QVector<QString> terminal;
                                terminal.push_back(derived.at(i));
                                (*it_tableRecord)->mergeFollowSet(terminal);
                                tmp_derivedLamda = false;
                            }
                            if (!tmp_derivedLamda) break;

                        }
                        if (derivedIndex + 1 == derivedSize) {
                            //qDebug() << "is last element";
                            tmp_derivedLamda = true;
                        }
                        if (tmp_derivedLamda
                                && table->currentFindingFollow() != (*it_searchTable)->key()) {
                            //qDebug() << "get follow set" << (*it_searchTable)->key();

                            (*it_tableRecord)->mergeFollowSet(table->followSet((*it_searchTable)->key()));

                        }

                    }

                }
            }

        }

    }
}

QVector<QString> TableRecord::followSet()
{
    if (!m_hasFollowSet) {
        Table *table = Table::instance();
        if (!table->m_currentFindingRecord.contains(key())) {
            table->m_currentFindingRecord.push_back(key());
            findFollowSet();
        }
        for (TableRecord::iterator it_tableRecord = begin();
             it_tableRecord != end();
             it_tableRecord ++) {
                mergeFollowSet((*it_tableRecord)->followSet());
        }
    }
    return m_followSet;
}

void TableRecord::mergeFirstSet(QVector<QString> firstSet)
{
    for (QVector<QString>::iterator it_firstSet = firstSet.begin();
         it_firstSet != firstSet.end();
         it_firstSet ++) {
        if (m_firstSet.indexOf(*it_firstSet) == -1) {
            m_firstSet.push_back(*it_firstSet);
        }
    }
    m_hasFirset = true;
}

void TableRecord::mergeFollowSet(QVector<QString> followSet)
{
    for (QVector<QString>::iterator it_followSet = followSet.begin();
         it_followSet != followSet.end();
         it_followSet ++) {
        if (m_followSet.indexOf(*it_followSet) == -1) {
            m_followSet.push_back(*it_followSet);
        }
    }
}

bool TableRecord::derivedLamda()
{
    bool result = false;
    int tableSize = size();
    for (int i = 0; i < tableSize; i ++) {
        result |= at(i)->derivedLamda();
    }
    return result;
}
<file_sep>/src/sourcecodehandler.h
#ifndef SOURCECODEHANDLER_H
#define SOURCECODEHANDLER_H

#include <QObject>
#include <QVector>
#include <QStack>

class SourceCodeHandler : public QObject
{
    Q_OBJECT
public:
    explicit SourceCodeHandler(QObject *parent = nullptr);
    QVector<int> process(QStringList line);
    void setStartPoint(QString startPoint) { m_startPoint = startPoint; }
    void showStackStatus();
    QVector<int> ruleApplyOrder() { return m_ruleApplyOrder; }
    bool isAccept() { return m_isAccept; }
signals:

public slots:
private:
    QStack<QString> m_parseStack;
    QString m_startPoint;
    QVector<int> m_ruleApplyOrder;
    bool m_isAccept;
};

#endif // SOURCECODEHANDLER_H
<file_sep>/src/table.cpp
#include "table.h"

#include <QDebug>
#include <iostream>


Table* Table::m_instance = nullptr;

Table::Table()
{

}

void Table::addRule(QString key, Rule* rule)
{
    Table::iterator it_table = find(key);
    m_ruleMap.insert(rule->ruleNumber(), rule);
    if (it_table != end()) {
        // Exist
        (*it_table)->push_back(rule);
    }
    else {
        // Not exist
        TableRecord *r = new TableRecord;
        r->push_back(rule);
        r->setKey(key);
        insert(key, r);
    }
}

Rule *Table::rule(int ruleNumber)
{
    QHash<int, Rule*>::iterator it_rule = m_ruleMap.find(ruleNumber);
    if (it_rule != m_ruleMap.end()) {
        return *it_rule;
    }
    return nullptr;
}

void Table::findFirstSet()
{
    for (Table::iterator it_table = begin();
         it_table != end();
         it_table ++) {
        (*it_table)->findFirstSet();
        // qDebug() << "TableRecord" << it_table.key() << (*it_table)->firstSet();
    }
}

QVector<QString> Table::firstSet(QString key)
{
    Table::iterator it_table = find(key);
    return (*it_table)->firstSet();
}

void Table::findFollowSet()
{
    for (Table::iterator it_table = begin();
         it_table != end();
         it_table ++) {
        m_currentFindingFollow = it_table.key();
        m_currentFindingRecord.push_back(it_table.key());
        (*it_table)->findFollowSet();
        m_currentFindingRecord.clear();
    }
}

QVector<QString> Table::followSet(QString key)
{
    Table::iterator it_table = find(key);
    return (*it_table)->followSet();
}

void Table::findPreictSet()
{
    for (Table::iterator it_table = begin();
         it_table != end();
         it_table ++) {

        for (TableRecord::iterator it_tableRecord = (*it_table)->begin();
             it_tableRecord != (*it_table)->end();
             it_tableRecord ++) {

            (*it_tableRecord)->findPredictSet();

        }

    }
}

bool Table::derivedLamda(QString key)
{
    Table::iterator it_table = find(key);
    return (*it_table)->derivedLamda();
}

Table* Table::instance()
{
    if (Table::m_instance == nullptr) {
        Table::m_instance = new Table;
    }
    return Table::m_instance;
}

void Table::printTable()
{
    for (Table::iterator it_table = begin();
         it_table != end();
         it_table ++) {

        for (TableRecord::iterator it_tableRecord = (*it_table)->begin();
             it_tableRecord != (*it_table)->end();
             it_tableRecord ++) {

            qDebug() << "Rule" << (*it_tableRecord)->ruleNumber() << ": \n"
                     << "Derived:" << (*it_tableRecord)->derived() << "\n"
                     << "First set: " << (*it_tableRecord)->firstSet() << "\n"
                     << "Derived lamda: " << (*it_tableRecord)->derivedLamda() << "\n"
                     << "Follow set: " << (*it_tableRecord)->followSet() << "\n"
                     << "Predict set: " << (*it_tableRecord)->predictSet() << "\n";

        }

    }
    for (Table::iterator it_table = begin();
             it_table != end();
             it_table ++) {

        for (TableRecord::iterator it_tableRecord = (*it_table)->begin();
             it_tableRecord != (*it_table)->end();
             it_tableRecord ++) {

            QVector<QString> m_derived = (*it_tableRecord)->derived();
            for (QVector<QString>::iterator it_derived = m_derived.begin();
                 it_derived != m_derived.end();
                 it_derived ++) {
                if (!isNonterminal(*it_derived) && (*it_derived) != "lamda") {
                    m_terminal.push_back(*it_derived);
                }
            }

        }

    }
    std::cout << "LL1 Table: "<< std::endl;
    for (Table::iterator it_table = begin();
             it_table != end();
             it_table ++) {
        for (TableRecord::iterator it_tableRecord = (*it_table)->begin();
             it_tableRecord != (*it_table)->end();
             it_tableRecord ++) {

            QVector<QString> predictSet = (*it_tableRecord)->predictSet();

            for (QVector<QString>::iterator it_terminal = m_terminal.begin();
                 it_terminal != m_terminal.end();
                 it_terminal ++) {
                if (predictSet.indexOf(*it_terminal) != -1) {
                    m_lookAheadTable.addRule((*it_table)->key(),
                                             *it_terminal,
                                             (*it_tableRecord)->ruleNumber());
                }
            }

        }

    }
    std::cout << "\t\t";
    for (QVector<QString>::iterator it_terminal = m_terminal.begin();
         it_terminal != m_terminal.end();
         it_terminal ++) {

        std::cout << (*it_terminal).toStdString().c_str() << "\t";

    }
    std::cout << std::endl;
    QHash<QString, QHash<QString, int>> lookAheadTable = m_lookAheadTable.table();
    for (QHash<QString, QHash<QString, int>>::iterator it_lookAheadTable = lookAheadTable.begin();
         it_lookAheadTable != lookAheadTable.end();
         it_lookAheadTable ++) {

        std::cout << it_lookAheadTable.key().toStdString().c_str() << "\t\t";
        for (QVector<QString>::iterator it_terminal = m_terminal.begin();
             it_terminal != m_terminal.end();
             it_terminal ++) {

            QHash<QString, int>::iterator it_row = (*it_lookAheadTable).find(*it_terminal);
            if (it_row != (*it_lookAheadTable).end()) {
                std::cout << *it_row << "\t";
            }
            else {
                std::cout << "\t";
            }

        }

        std::cout << std::endl;

    }
}

void Table::addTerminal(QString terminal)
{
    if (m_terminal.indexOf(terminal) == -1) {
        m_terminal.push_back(terminal);
    }
}
<file_sep>/src/ffparser.cpp
#include "ffparser.h"

#include <QCoreApplication>
#include <QDebug>
#include <iostream>
#include <QRegExp>

#include "rule.h"
#include "tablerecord.h"
#include "table.h"
#include "sourcecodehandler.h"

FFParser::FFParser(QObject *parent) : QObject(parent)
{
    QCoreApplication *app = QCoreApplication::instance();
    m_arguments = app->arguments();
    for (QStringList::iterator it_arguments = m_arguments.begin();
         it_arguments != m_arguments.end();
         it_arguments ++) {
        if ((*it_arguments) == "-o" || (*it_arguments) == "--output") {
            m_outputFilename = *(it_arguments + 1);
        }
        else if ((*it_arguments) == "-s" || (*it_arguments) == "--source") {
            m_sourceCodeFile.setFileName(*(it_arguments + 1));
        }
        else if ((*it_arguments) == "-c" || (*it_arguments) == "--cfg") {
            m_cfgFile.setFileName(*(it_arguments + 1));
        }
    }
}

void FFParser::run()
{
    if (!m_sourceCodeFile.exists()) {
        qFatal("Source code file is required");
    }
    if (!m_cfgFile.exists()) {
        qFatal("CFG file is required.");
    }
    buildTable();
    Table *table = Table::instance();
    table->findFirstSet();
    table->findFollowSet();
    table->findPreictSet();
    table->printTable();
    readSourceCodeLine();

    for (QVector<QStringList>::iterator it_line = m_sourceCodeLine.begin();
         it_line != m_sourceCodeLine.end();
         it_line ++) {

        SourceCodeHandler handler;
        handler.setStartPoint(m_startPoint);
        handler.process(*it_line);
        QVector<int> result = handler.ruleApplyOrder();

        for (QStringList::iterator it_rawLine = (*it_line).begin();
             it_rawLine != (*it_line).end();
             it_rawLine ++) {
            std::cout << (*it_rawLine).toStdString().c_str() << " ";
        }

        for (QVector<int>::iterator it_result = result.begin();
             it_result != result.end();
             it_result ++) {
            std::cout << (*it_result) << " ";
        }

        handler.isAccept() ? std::cout << std::endl : std::cout << "Error" << std::endl;

    }

}

void FFParser::buildTable()
{
    if (!m_cfgFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qFatal("CFG file cannot be opened.");
    }
    QString currentToken;
    while (!m_cfgFile.atEnd()) {
        QString line(m_cfgFile.readLine());
        line = line.trimmed();
        QStringList tokens = line.split(QRegExp("[ \t\r\n\f]"));
        Rule *r = new Rule();
        r->setRuleNumber(tokens.at(0).toInt());
        if (tokens.at(0).toInt() == 1) {
            m_startPoint = tokens.at(1);
        }
        if (tokens.at(1) != QString("|")) {
            currentToken = tokens.at(1);
            for (int i =3; i < tokens.size(); i ++) {
                if (tokens.at(i) == "lamda") continue;
                r->addDerived(tokens.at(i));
            }
        }
        else {
            for (int i =2; i < tokens.size(); i ++) {
                if (tokens.at(i) == "lamda") continue;
                r->addDerived(tokens.at(i));
            }
        }
        Table *table = Table::instance();
        table->addRule(currentToken, r);
    }
}

void FFParser::readSourceCodeLine()
{
    while(!m_sourceCodeFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qFatal("Source code file cannot be opened");
    }
    while(!m_sourceCodeFile.atEnd()) {
        QString line(m_sourceCodeFile.readLine());
        line = line.trimmed();
        QStringList tokens = line.split(QRegExp("[ \t\r\n\f]"));
        m_sourceCodeLine.push_back(tokens);
    }
}
<file_sep>/src/sourcecodehandler.cpp
#include "sourcecodehandler.h"
#include "table.h"
#include "lookaheadtable.h"

#include <QDebug>
#include <iostream>

SourceCodeHandler::SourceCodeHandler(QObject *parent)
    : QObject(parent)
    , m_isAccept(false)
{

}

QVector<int> SourceCodeHandler::process(QStringList line)
{
    QVector<int> result;

    Table *table = Table::instance();
    LookAheadTable *lookAheadTable = table->lookAheadTable();

    m_parseStack.push(m_startPoint);

    while(!m_parseStack.isEmpty() && line.size() != 0) {
        if (table->isNonterminal(m_parseStack.top())) {
            QString top = m_parseStack.top();
            int ruleNumber = lookAheadTable->findRule(top, line.at(0));
            if (ruleNumber == -1) { break; }
            m_ruleApplyOrder.push_back(ruleNumber);
            qDebug() << "Apply rule number" << ruleNumber;
            Rule* applyRule = table->rule(ruleNumber);
            QVector<QString> derived = applyRule->derived();
            if (derived.size() > 0) {
                m_parseStack.pop();
                for (QVector<QString>::reverse_iterator rit_derived = derived.rbegin();
                     rit_derived != derived.rend();
                     rit_derived ++) {
                    m_parseStack.push_back(*rit_derived);
                }
                while( ((!m_parseStack.isEmpty())&&(line.size()!=0))
                        &&(m_parseStack.top() == line.at(0)) )
                {
                    qDebug() << "matched" << m_parseStack.top();
                    m_parseStack.pop();
                    line.pop_front();
                }
            }
            else {
                m_parseStack.pop();
            }
        }
        else {
            if (m_parseStack.top() == line.at(0)) {
                while( ((!m_parseStack.isEmpty())&&(line.size()!=0))
                       &&(m_parseStack.top() == line.at(0)) )
                {
                    qDebug() << "matched!" << m_parseStack.top();
                    m_parseStack.pop();
                    line.pop_front();
                }
            }
            else {
                break;
            }
        }
    }

    if (m_parseStack.isEmpty() && line.size() == 0) {
        qDebug() << "Accept\n";
        m_isAccept = true;
    }
    else {
        qDebug() << "Error\n";
        m_isAccept = false;
    }

    return result;
}

void SourceCodeHandler::showStackStatus()
{
    for (QStack<QString>::iterator it_parseStack = m_parseStack.begin();
         it_parseStack != m_parseStack.end();
         it_parseStack ++) {
        std::cout << (*it_parseStack).toStdString().c_str() << " ";
    }
    std::cout << std::endl;
}
<file_sep>/src/lookaheadtable.cpp
#include "lookaheadtable.h"

LookAheadTable::LookAheadTable(QObject *parent) : QObject(parent)
{

}

void LookAheadTable::addRule(QString nonterminal, QString terminal, int ruleNumber)
{
    QHash<QString, QHash<QString,int>>::iterator it_terminalMap
            = m_lookAheadTable.find(nonterminal);

    if (it_terminalMap != m_lookAheadTable.end()) {
        (*it_terminalMap).insert(terminal, ruleNumber);
    }
    else {
        QHash<QString, int> newRow;
        newRow.insert(terminal, ruleNumber);
        m_lookAheadTable.insert(nonterminal, newRow);
    }
}

int LookAheadTable::findRule(QString source, QString target)
{
    QHash<QString, QHash<QString,int>>::iterator it_terminalMap
            = m_lookAheadTable.find(source);

    QHash<QString, int>::iterator it_row = (*it_terminalMap).find(target);
    if (it_row != (*it_terminalMap).end()) {
        return *it_row;
    }
    else return -1;
}
<file_sep>/src/table.h
#ifndef TABLE_H
#define TABLE_H

#include <QObject>
#include <QHash>
#include <QString>

#include "tablerecord.h"
#include "rule.h"
#include "lookaheadtable.h"

class Table : public QHash<QString, TableRecord*>
{
public:
    explicit Table();
    void addRule(QString key, Rule* rule);
    Rule* rule(int ruleNumber);
    void findFirstSet();
    void findFollowSet();
    void findPreictSet();
    QVector<QString> firstSet(QString key);
    QVector<QString> followSet(QString key);
    static Table* instance();
    bool isNonterminal(QString key) { return (find(key) != end()); }
    bool derivedLamda(QString key);
    void printTable();
    void addTerminal(QString terminal);
    QString currentFindingFollow() { return m_currentFindingFollow; }
    LookAheadTable* lookAheadTable() { return &m_lookAheadTable; }
    QVector<QString> m_currentFindingRecord;
signals:

public slots:
private:
    static Table* m_instance;
    QVector<QString> m_terminal;
    QHash<int, Rule*> m_ruleMap;
    LookAheadTable m_lookAheadTable;
    QString m_currentFindingFollow;
};

#endif // TABLE_H
<file_sep>/src/ffparser.h
#ifndef FFPARSER_H
#define FFPARSER_H

#include <QObject>
#include <QStringList>
#include <QFile>
#include "rule.h"
#include "table.h"

class FFParser : public QObject
{
    Q_OBJECT
public:
    explicit FFParser(QObject *parent = nullptr);
    void run();
    void buildTable();
    void readSourceCodeLine();
signals:

public slots:
private:
    QStringList m_arguments;
    QString m_outputFilename;
    QFile m_cfgFile;
    QFile m_sourceCodeFile;
    QVector<QStringList> m_sourceCodeLine;
    QString m_startPoint;
};

#endif // FFPARSER_H
