очень подробно опиши код package main

import (
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

// CFG структура для работы с контекстно-свободной грамматикой
type CFG struct {
	rules           map[string][]string //карта, где ключи — это нетерминалы, а значения — списки возможных продукций
	terminals       map[string]bool //карта, содержащая конечные символы
	variables       map[string]bool // содержит нетерминалы переменные
	startVariable   string //начальный нетерминал
	maxDepth        int //макс глубина рекурсии
	maxLength       int //макс длина строки
	endSymbol       string //символ в конце строки
	generateCounter int //кол-во попыток генерации
	generationSteps int //кол-во шагов генерации
	validationSteps int
	comment         string
}

// NewCFG инициализирует новый объект CFG
func NewCFG(rulesFile string, maxDepth int) *CFG {
	cfg := &CFG{
		rules:     make(map[string][]string),
		terminals: make(map[string]bool),
		variables: make(map[string]bool),
		maxDepth:  maxDepth,
		maxLength: 50,
		endSymbol: ".",
	}
	cfg.loadRules(rulesFile)
	return cfg
}

// loadRules загружает правила из файла
func (cfg *CFG) loadRules(rulesFile string) {
	file, err := os.ReadFile(rulesFile)
	if err != nil {
		fmt.Println("Ошибка при чтении файла:", err)
		return
	}

	lines := strings.Split(string(file), "\n")
	section := ""

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if len(line) == 0 || strings.HasPrefix(line, "#") {
			cfg.comment = line
			fmt.Println(cfg.comment)
			continue
		}

		if strings.HasPrefix(line, "env:") {
			section = "env"
		} else if strings.HasPrefix(line, "G") {
			section = "G"
			parts := strings.Split(line, "{")
			terminals := strings.TrimRight(parts[1], "}")
			variables := strings.TrimRight(parts[2], "}")

			for _, terminal := range strings.Split(terminals, ", ") {
				cfg.terminals[terminal] = true
			}
			for _, variable := range strings.Split(variables, ", ") {
				cfg.variables[variable] = true
			}
		} else if strings.HasPrefix(line, "Pn:") {
			section = "Pn"
		} else if strings.HasPrefix(line, "Pt:") {
			section = "Pt"
		} else if section == "Pn" {
			parts := strings.Split(line, "->")
			nonTerminal := strings.TrimSpace(parts[0])
			transformations := strings.Split(parts[1], "|")
			for i := range transformations {
				transformations[i] = strings.TrimSpace(transformations[i])
			}
			cfg.rules[nonTerminal] = transformations
			if cfg.startVariable == "" {
				cfg.startVariable = nonTerminal
			}
		} else if section == "Pt" {
			parts := strings.Split(line, "->")
			terminal := strings.TrimSpace(parts[0])
			transformations := strings.Split(parts[1], "|")
			for i := range transformations {
				transformations[i] = strings.TrimSpace(transformations[i])
			}
			cfg.rules[terminal] = transformations
		} else if section == "env" {
			parts := strings.Split(strings.TrimPrefix(line, "env: "), "|")
			if len(parts) > 0 {
				cfg.startVariable = strings.TrimSpace(parts[0])
			}
			if len(parts) > 1 {
				cfg.maxLength = stringToInt(strings.TrimSpace(parts[1]), 50)
			}
			if len(parts) > 2 {
				cfg.endSymbol = strings.TrimSpace(parts[2])
			}
		}
	}
	if len(cfg.rules) == 0 {
		fmt.Println("Ошибка: отсутствуют правила продукции (Pn).")
	}
}

// stringToInt безопасно конвертирует строку в целое число с значением по умолчанию
func stringToInt(s string, defaultValue int) int {
	val, err := strconv.Atoi(s)
	if err != nil {
		return defaultValue
	}
	return val
}

// generate генерирует строку, начиная с указанного символа
func (cfg *CFG) generate(symbol string, depth int) (string, int, float64, int) {
	if symbol == "" {
		symbol = cfg.startVariable
	}

	startTime := time.Now()
	result := ""
	cfg.generationSteps = 0

	for {
		cfg.generateCounter++
		result = cfg.generateRecursive(symbol, depth)

		if len(result) < cfg.maxLength {
			totalTime := time.Since(startTime).Seconds()
			return result + cfg.endSymbol, cfg.generateCounter, totalTime, cfg.generationSteps
		} else {
			fmt.Printf("Предупреждение: строка слишком длинная (%d символов): %s\n", len(result), result)
		}
	}
}

// generateRecursive рекурсивно генерирует строку
func (cfg *CFG) generateRecursive(symbol string, depth int) string {
	cfg.generationSteps++

	// Проверка на максимальную глубину рекурсии
	if depth > cfg.maxDepth {
		return ""
	}

	// Если символ является терминалом или оператором, возвращаем его
	if cfg.terminals[symbol] || strings.ContainsAny(symbol, "+-*/()") {
		return symbol
	}

	// Если символ является нетерминалом, ищем его продукцию
	if productions, ok := cfg.rules[symbol]; ok {
		chosenIndex := rand.Intn(len(productions))   // Случайный выбор правила
		chosenProduction := productions[chosenIndex] // Выбранное правило

		// Выводим информацию о текущем шаге
		fmt.Printf("%s -> %s (Выбрано правило #%d)\n", symbol, chosenProduction, chosenIndex+1)

		var result strings.Builder
		for _, sym := range strings.Fields(chosenProduction) {
			// Рекурсивный вызов для каждого символа в правиле
			result.WriteString(cfg.generateRecursive(sym, depth+1) + " ")
		}

		return strings.TrimSpace(result.String()) // Возвращаем сгенерированную строку без лишнего пробела
	}

	// Если нет подходящего правила, возвращаем пустую строку
	return ""
}

// main функция для запуска программы
func main() {
	cfg := NewCFG("rules.txt", 10)
	result, attempts, duration, steps := cfg.generate("", 0)
	fmt.Printf("Сгенерированная строка: %s\nПопытки: %d, Время: %.6f секунд, Шаги: %d\n", result, attempts, duration, steps)
}