Нумерация лексем в лексическом анализаторе

Пишу программу для выполнения фазы лексического анализатора для выражения foreach (... in ...). Нужно получить таблицу с информацией об элементах и финальную последовательность лексем.

Вот код:

#define _CRT_SECURE_NO_WARNINGS

#include <locale.h>
#include <iostream>
using namespace std;

char KEYWORDS[2][256] = { (char*)"foreach", (char*)"in" };

int main()
{
    setlocale(LC_ALL, "rus");
    string final_str = "";
    int state = 0; // Переменная, отвечающая за текущее состояние
    int symbol, num = 1;
    FILE* textfile = fopen("text.txt", "r");

    symbol = fgetc(textfile);
    while (!feof(textfile))
    {
        switch (state)
        {
        case 0:
        {
            while ((symbol == ' ') || (symbol == '\n'))
            {
                symbol = fgetc(textfile);
            }
            if (symbol >= 'a' && symbol <= 'z') 
                state = 1;
            else if (symbol >= '0' && symbol <= '9')
                state = 2;
            else if (symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/' || symbol == '{' || symbol == '}' || symbol == '(' || symbol == ')' || symbol == '>' || symbol == '<' || symbol == '=' || symbol == ';')
                state = 3;
            else
                state = 4;
            break;
        }
        case 1: // Идентификаторы и ключевые слова
        {
            char buf[256];
            int size = 0;
            while ((symbol >= 'a' && symbol <= 'z') || (symbol >= 'A' && symbol <= 'Z'))
            {
                buf[size] = symbol;
                size++;
                symbol = fgetc(textfile);
            }
            int flag1 = 0, flag2 = 0;
            for (int i = 0; i < size; i++)
            {
                if (buf[i] != KEYWORDS[0][i])
                    flag1 = 1;
                if (buf[i] != KEYWORDS[1][i])
                    flag2 = 1;
            }
            if (flag1 == 0 || flag2 == 0)
            {
                cout << num << ".\t";
                for (int i = 0; i < size; i++)
                    cout << buf[i];
                cout << "\t\tключевое слово\n";
                final_str += " <ключ.слово";
                if (flag1 == 0)
                    final_str += "1";
                else
                    final_str += "2";
                final_str += "> ";
            }
            else
            {
                cout << num << ".\t";
                for (int i = 0; i < size; i++)
                    cout << buf[i];
                cout << "\t\tидентификатор\n";
                final_str += " <ид";
                if (num / 10 == 0)
                    final_str += (char)(num + 48);
                else
                {
                    final_str += (char)(num / 10 + 48);
                    final_str += (char)(num % 10 + 48);
                }
                final_str += "> ";
            }
            num++;
            state = 0;
            break;
        }
        case 2: // Числа
        {
            char buf[256];
            int size = 0;
            while (symbol >= '0' && symbol <= '9')
            {
                buf[size] = symbol;
                size++;
                symbol = fgetc(textfile);
            }
            cout << num << ".\t";
            for (int i = 0; i < size; i++)
                cout << buf[i];
            cout << "\t\tчисло\n";
            final_str += " <число";
            if (num / 10 == 0)
                final_str += (char)(num + 48);
            else
            {
                final_str += (char)(num / 10 + 48);
                final_str += (char)(num % 10 + 48);
            }
            final_str += "> ";
            num++;
            state = 0;
            break;
        }
        case 3: // Лексемы
        {
            cout << num << ".\t" << (char)symbol << "\t\tлексема\n";
            num++;
            final_str += symbol;
            state = 0;
            symbol = fgetc(textfile);
            break;
        }
        case 4:
            system("cls");
            cout << "Введены ошибочные данные" << endl;
            return 0;
        }
    }
    cout << endl << final_str << endl;
    
    fclose(textfile);
    system("pause");
    return 0;
}

Таблицу вывода сделал, но с последовательностью не получается (в качестве заглушки пока сделал вывод номера рассматриваемой лексемы).

Например для файла с содержимым

foreach (item in items) {
item = item + 1;
}

нужно получить такую последовательность:

<ключ.слово1> (<ид1> <ключ.слово2> <ид2>) { <ид1> = <ид1> + <число1>;}

Как можно сделать правильный вывод последовательности?


Ответы (1 шт):

Автор решения: KoVadim

Давайте поправим явную ошибку в строке

char KEYWORDS[2][256] = { (char*)"foreach", (char*)"in" };

на такой код

const char KEYWORDS[2][256] = { "foreach", "in" };

студийный компилятор раньше закрывал глаза на это, но лучше писать хорошо сразу.

Теперь перейдем к айдишникам. Что бы выводить красиво, нужно где то запоминать номера. Я сделал через map, но посмотрев на код, мне показалось, что там к коду могут быть другие требования, так что если что, переписывайте сами так, как хочется. Я бы честно сильно-сильно отрефакторил весь код и разделил на части, а не смешивал в одну кучу.

Вначале добавим вверху такое

#include <map>

и в начале main

std::map<std::string, int> vars;

мы будем тут хранить список найденных переменных (идентификаторов?)

        else
        {
            cout << num << ".\t";
            for (int i = 0; i < size; i++)
                cout << buf[i];
            std::string id{buf, (size_t)size};
            int no = -1;
            if (vars.count(id) == 0) {
                // переменной нет, добавим
                // номер - просто кол-во переменных +1
                no = vars.size()+1;
                vars[id] = no;
            }
            else {
                // нашли переменную
                no = vars[id];
            }
            cout << "\t\tидентификатор\n";
            final_str += " <ид";
            if (no / 10 == 0)
                final_str += (char)(no + 48);
            else
            {
                final_str += (char)(no / 10 + 48);
                final_str += (char)(no % 10 + 48);
            }
            final_str += "> ";
        }

у меня получился вот такой вывод

<ключ.слово1> ( <ид1>  <ключ.слово2>  <ид2> ){ <ид1> = <ид1> + <число12> ;}

как минимум с ид все хорошо. с числом - тут я не знаю, как оно должно быть. Возможно просто отдельный счетчик и все.

На будущее можно сделать отдельную структуру для идентификаторов и в мапе можно хранить уже эти структуры.

→ Ссылка