Как создать синтаксический анализатор на С++

как создать программу на С++, которая переводила, например, бы этот текст:

main(form(text(hello())text1(new)))

в этот:

main form text hello 
main form text1 new

я уже пытался это сделать, но пока не получилось:

#include <iostream>
#include <vector>

using namespace std;


int main()
{
    string code = "main(form(text(hello(world))text1(new)))";

            vector<string> real_code;
            string token;

            for(char k: code)
            {
                if(k == '(')
                {
                    real_code.push_back(token);
                    real_code.push_back("(");
                    token = "";
                }else if(k == ')')
                {
                    real_code.push_back(token);
                    real_code.push_back(")");
                    token = "";
                }else{token += k;}
            }

    for(string j: real_code)
    {
        cout << j << " ";
    }cout << endl << endl;

    vector<string> names;

    for(int i = 0; i < 11; i++)
    {
        string here = real_code[i];
        string next = (i < 10)? real_code[i+1] : real_code[i];

        cout << "\n=> ";
        if(here == ")")
        {
            for(string j: names)
            {
                cout << j << " ";
            }//cout << endl;
            names.pop_back();
        } else if(here == "("){
            continue;
        } else {
            if(next == ")")
            {
                names.push_back(here);
                for(string j: names)
                {
                    cout << j << " ";
                }
            }
            if(next == "(")
            {
                names.push_back(here);
            }
        }
    }
}

(Заранее простите за говнокод)


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

Автор решения: AYE777
#include <string>
#include <sstream>
#include <vector>

using namespace std;

vector<string> parseInput(const string& input) {
    vector<string> result;
    string current;
    
    for (char c : input) {
        if (c == '(' || c == ')') {
            if (!current.empty()) {
                result.push_back(current);
                current.clear();
            }
        } else {
            current += c;
        }
    }
    
    if (!current.empty()) {
        result.push_back(current);
    }
    
    return result;
}

void printParsed(const vector<string>& parsed) {
    vector<string> stack;
    
    for (const string& token : parsed) {
        if (!token.empty()) {
            stack.push_back(token);
            for (const string& s : stack) {
                cout << s << " ";
            }
            cout << endl;
        }
    }
}

int main() {
    string input = "main(form(text(hello())text1(new)))";
    vector<string> parsed = parseInput(input);
    printParsed(parsed);
    
    return 0;
}

Вывод:

main form 
main form text 
main form text hello 
main form text1 
main form text1 new 

→ Ссылка
Автор решения: Matvj

Вам потребуются следующие заголовки: vector, optional*, string и iostream.

Пусть у нас будет перечисление

enum class TokenType
{
    ident,
    open_par,
    close_par,
}

Теперь у нас есть перечисление токенов, вы можете добавлять туда нужные вам типы токенов, пока что я добавил только, те которые были нужны вам сейчас.

Пусть у нас будет структура токена

struct Token
{
    std::optional<std::string> value;
    TokenType type;
}

Пусть у нас есть функция для преобразования токенов в строку.

std::string token_to_string(Token token)
{
    if(token.type == TokenType::ident)
        return token.value.value(); 
    else if(token.type == TokenType::open_pra)
        return " "; 
    else
        return "";
}

Теперь вы можете добавлять или изменять вывод текста. Допустим вы захотите, чтобы open_pra был текст, допустим (, а не пробел. Тогда вам надо просто заменить " " на "("

Теперь пусть будет обратная функция для преобразования строки в токен

Token string_to_token(std::string str)
{
    Token token;

    if(str == "(")
    {
        toke.type = TokenType::open_pra;
    }

    else if(str == ")")
    {
        token.type = TokenType::close_pra;
    }
    
    else
    {
        token.type = TokenType::ident;
        token.value = str;
    }

    return token;
}

Вы также можете расширять эту функцию по мере расширения перечисления.

Теперь самое главное - превращение кода в токены. За это будет отвечать функция tokenizer.

std::vector<Token> tokenizer(std::string code)
{
    std::vector<Token> tokens;
    std::string buffer;

    for(size_t i = 0; i < code.size(); i++)
    {
        char symbol = code.at(i);

        if(std::isalpha(symbol))
        {
           buffer.push_back(symbol);
           i++;

           while(i < code.size() && std::isalnum(code.at(i)))
           {
               buffer.push_back(code.at(i));
               i++;
           }
           i--;

           tokens.push_back(string_to_token(buffer));
           buffer.clear();
        }

        else if(symbol == '(')
        {
            tokens.push_back(Token{.value = "(", .type = TokenType::open_pra});                
        }

        else if(symbol == ')')
        {
            tokens.push_back(Token{.value = ")", .type = TokenType::close_pra});
        }

        else 
        {
            // Можете добавить свой обработчик ошибок при не определённой лексеме, всё же ответы не об этом
            std::cerr << "Unknown lexeme\n";
            exit(EXIT_FAILUERE);
        }
    }

    return tokens;
}

Теперь мы можем приврать код в вектор токенов с помощью этой функции. Осталась только функция main.

int main()
{
    std::string code = "main(form(text(hello())text1(new)))";

    std::vector<Token> tokens = tokenizer(code);

    for(size_t i = 0; i < tokens.size(); i++)
    {
        Token token = tokens.at(i);
        
        if(token.type == TokenType::close_pra && i + 1 < tokens.size() && tokens.at(i + 1))
        {
            std::cout << "\n";
            i += 2;
        }

        else 
        {
            std::cout << token_to_string(token);
        }
    }

    return 0;
}

optional* - для использования optional вам потребуется С++17 или новее.

→ Ссылка