package main

import (
	"fmt"
	"math"
	"strconv"
	"strings"
)

type TokenType int

const (
	Number TokenType = iota
	Operator
	Variable
	Function
	ArgumentSeparator
)

type Token struct {
	Type  TokenType
	Value string
}

var variables = make(map[string]float64)

func parseExpression(expression string) ([]Token, error) {
	tokens := []Token{}
	var buffer strings.Builder

	for _, ch := range expression {
		if ch == ' ' {
			continue
		}

		if strings.ContainsRune("+-/^()!", ch) {
			if buffer.Len() > 0 {
				token, err := createToken(buffer.String())
				if err != nil {
					return nil, err
				}
				tokens = append(tokens, token)
				buffer.Reset()
			}
			tokens = append(tokens, Token{Type: Operator, Value: string(ch)})
		} else if ch == ',' {
			if buffer.Len() > 0 {
				token, err := createToken(buffer.String())
				if err != nil {
					return nil, err
				}
				tokens = append(tokens, token)
				buffer.Reset()
			}
			tokens = append(tokens, Token{Type: ArgumentSeparator, Value: string(ch)})
		} else {
			buffer.WriteRune(ch)
		}
	}

	if buffer.Len() > 0 {
		token, err := createToken(buffer.String())
		if err != nil {
			return nil, err
		}
		tokens = append(tokens, token)
	}

	return tokens, nil
}

func createToken(value string) (Token, error) {
	if _, err := strconv.ParseFloat(value, 64); err == nil {
		return Token{Type: Number, Value: value}, nil
	}

	if _, ok := variables[value]; ok {
		return Token{Type: Variable, Value: value}, nil
	}

	if value == "root" {
		return Token{Type: Function, Value: value}, nil
	}

	return Token{}, fmt.Errorf("неизвестный токен: %s", value)
}

func compute(tokens []Token) (float64, []Token, error) {
	if len(tokens) == 0 {
		return 0, tokens, fmt.Errorf("ожидался токен")
	}

	token := tokens[0]
	tokens = tokens[1:]

	switch token.Type {
	case Number:
		value, _ := strconv.ParseFloat(token.Value, 64)
		return value, tokens, nil
	case Variable:
		value := variables[token.Value]
		return value, tokens, nil
	case Operator:
		left, leftoverTokens, err := compute(tokens)
		if err != nil {
			return 0, tokens, err
		}
		tokens = leftoverTokens

		if token.Value == "(" {
			value, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			if len(leftoverTokens) == 0 || leftoverTokens[0].Value != ")" {
				return 0, tokens, fmt.Errorf("ожидалась закрывающая скобка")
			}
			tokens = leftoverTokens[1:]
			return value, tokens, nil
		}

		if token.Value == "+" {
			right, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			return left + right, leftoverTokens, nil
		} else if token.Value == "-" {
			right, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			return left - right, leftoverTokens, nil
		} else if token.Value == "" {
			right, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			return left * right, leftoverTokens, nil
		} else if token.Value == "/" {
			right, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			return left / right, leftoverTokens, nil
		} else if token.Value == "^" {
			right, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			return math.Pow(left, right), leftoverTokens, nil
		} else if token.Value == ")" {
			return left, tokens, fmt.Errorf("неожиданная закрывающая скобка")
		}
	case Function:
		if token.Value == "root" {
			if len(tokens) == 0 {
				return 0, tokens, fmt.Errorf("ожидался аргумент функции")
			}

			if tokens[0].Value != "(" {
				return 0, tokens, fmt.Errorf("ожидалась открывающая скобка")
			}
			tokens = tokens[1:]

			base, leftoverTokens, err := compute(tokens)
			if err != nil {
				return 0, tokens, err
			}
			if len(leftoverTokens) == 0 || leftoverTokens[0].Type != ArgumentSeparator {
				return 0, tokens, fmt.Errorf("ожидался разделитель аргументов ‘,’")
			}

			expTokens := leftoverTokens[1:]
			exponent, leftoverTokens, err := compute(expTokens)
			if err != nil {
				return 0, tokens, err
			}

			if len(leftoverTokens) == 0 || leftoverTokens[0].Value != ")" {
				return 0, tokens, fmt.Errorf("ожидалась закрывающая скобка")
			}
			tokens = leftoverTokens[1:]

			return math.Pow(base, 1/exponent), tokens, nil
		}
	case ArgumentSeparator:
		return 0, tokens, fmt.Errorf("неожиданный разделитель аргументов ','")
	}

	return 0, tokens, fmt.Errorf("необработанный токен: %v", token)
}

func main() {
	variables["x"] = 5

	expression := "3 ^ 2 + root(27, 3) + x"

	tokens, err := parseExpression(expression)
	if err != nil {
		fmt.Printf("Ошибка при парсинге выражения: %v\n", err)
		return
	}

	result, _, err := compute(tokens)
	if err != nil {
		fmt.Printf("Ошибка при вычислении выражения: %v\n", err)
	} else {
		fmt.Printf("Результат вычисления: %.4f\n", result)
	}
}

При вычислении "3 ^ 2 + root(27, 3) + x" результат вычисления: 3.0000 что не корректно