@RetroCR

Почему не работает выражение в интерпретаторе?

Стоит задача, написать интерпретатор на C++. Почему-то он не считает корень "z = sqrt(x);".

Вот код:
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <cmath>
#include <stdexcept>

using namespace std;

enum DataType { INT, FLOAT };

struct Variable {
    DataType type;
    union {
        int intValue;
        float floatValue;
    } value;
};

unordered_map<string, Variable> variables;

float to_float(const Variable& var) {
    return var.type == INT ? var.value.intValue : var.value.floatValue;
}

int to_int(const Variable& var) {
    if (var.type == FLOAT) throw runtime_error("Cannot convert float to int");
    return var.value.intValue;
}

Variable parse_value(const string& str) {
    Variable result;
    if (str.find('.') == string::npos) {
        result.type = INT;
        result.value.intValue = stoi(str);
    }
    else {
        result.type = FLOAT;
        result.value.floatValue = stof(str);
    }
    return result;
}

Variable apply_math_function(const string& func, const Variable& arg) {
    if (arg.type == INT) throw invalid_argument("Math functions require float arguments");

    float argValue = arg.value.floatValue;
    Variable result;
    result.type = FLOAT;

    if (func == "sqrt") result.value.floatValue = sqrt(argValue);
    else if (func == "sin") result.value.floatValue = sin(argValue);
    else if (func == "cos") result.value.floatValue = cos(argValue);
    else throw invalid_argument("Unknown function: " + func);

    return result;
}

Variable perform_operation(Variable lhs, char op, Variable rhs) {
    Variable result;

    if (lhs.type == FLOAT || rhs.type == FLOAT) {
        float left = to_float(lhs);
        float right = to_float(rhs);

        result.type = FLOAT;
        switch (op) {
        case '+': result.value.floatValue = left + right; break;
        case '-': result.value.floatValue = left - right; break;
        case '*': result.value.floatValue = left * right; break;
        case '/': result.value.floatValue = left / right; break;
        case '^': result.value.floatValue = pow(left, right); break;
        default: throw invalid_argument("Unknown operator: " + string(1, op));
        }
    }
    else {
        int left = to_int(lhs);
        int right = to_int(rhs);

        result.type = INT;
        switch (op) {
        case '+': result.value.intValue = left + right; break;
        case '-': result.value.intValue = left - right; break;
        case '*': result.value.intValue = left * right; break;
        case '/': result.value.intValue = left / right; break;
        case '^': result.value.intValue = pow(left, right); break;
        default: throw invalid_argument("Unknown operator: " + string(1, op));
        }
    }

    return result;
}

Variable eval_expr(string expr) {
    istringstream iss(expr);
    Variable result = parse_value("0");
    char op = '+';
    string segment, func;

    while (iss >> segment) {
        if (segment == "+" || segment == "-" || segment == "*" || segment == "/" || segment == "^") {
            op = segment[0];
        }
        else if (segment == "sqrt" || segment == "sin" || segment == "cos") {
            func = segment;
        }
        else if (isdigit(segment[0]) || segment.find('.') != string::npos) {
            Variable value = parse_value(segment);
            if (!func.empty()) {
                value = apply_math_function(func, value);
                func = "";
            }
            result = perform_operation(result, op, value);
        }
        else if (variables.find(segment) != variables.end()) { 
            Variable value = variables[segment];
            result = perform_operation(result, op, value);
        }
    }

    return result;
}

int main() {
    string script = R"(
        x = 5;
        x = x + 1;
        y = x * x + 12.5;
        z = sqrt(x);
    )";

    istringstream iss(script);
    string line;

    while (getline(iss, line)) {
        size_t eqPos = line.find('=');

        if (eqPos != string::npos) {
            string var = line.substr(0, eqPos);
            var.erase(remove_if(var.begin(), var.end(), ::isspace), var.end());
            string expr = line.substr(eqPos + 1);
            variables[var] = eval_expr(expr);
        }
    }

    for (const auto& kv : variables) {
        cout << kv.first << ": ";
        if (kv.second.type == INT)
            cout << kv.second.value.intValue;
        else
            cout << kv.second.value.floatValue;
        cout << endl;
    }

    return 0;
}
  • Вопрос задан
  • 108 просмотров
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Отладчик есть? Или хотя бы отладочный вывод добавьте. Выводите все переменные там, где они меняются. Мне кажется, что вы из строк все пробелы удаляете и у вас segment получается "sqrt(x);". Ведь stringstream будет читать одно слово целиком до пробельного символа, а их там тупо нет. И потом проверка segment == "sqrt" не срабатывает. Но мне лень ваш код запускать, перепроверьте эту гипотезу сами.

Вообще у вас тут этап токенизации пропущен. Надо сначала разбить входную строку на токены - имена переменных, числа, функции, операторы, скобки, точки с запятой - вот все сущности, что у вас есть в программе. Уже по этому потом разбирать. Вы же пытаетесь и разбор выражения и токенизацию вперемешку сделать и у вас то тут то там что-то ломается.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы