Символьный Калькулятор на С++. Проблема с возведением в степень

Есть задача выполнить калькулятор, принимающий строки, состоящие из

  • символов '0'-'9',
  • скобок '(', ')',
  • символов '+', '-', '*', '/', '^',

и выполняющий действия над одноразрядными операндами

  • сложения
  • вычитания
  • умножения
  • целочисленного деления
  • возведения в степень.

Архитектура решения должна содержать рекурсивный спуск.

Удалось написать код:

#include <stdio.h>
#include <setjmp.h>

jmp_buf begin;
char curlex;

void getlex(void); 
int expr(void); 
int add_sub(void); 
int mult_div(void); 
int power(void);
void error(); 

int main() {
    int result; /*Переменная результата вычисления*/
    setjmp(begin); /*сохранения всех переменных окружения*/
    printf("==>"); 
    getlex();      /*Вызов функции чтения символов ввода: 
                     получает первый значащий символ в переменную curlex*/
    result=expr(); /*Вычисление результата с помощью функции*/
    if ( curlex != '\n') error(); /*Выброс ошибки и повтор ввода выражения
                                   если в потоке попались не 0-9, символы арифметики
                                   скобки или перевод*/
    printf("\n%d\n",result);
    return 0;
}
/*Функция принимает поток символов и записывает их в глобальную переменную*/
void getlex() {
while ( ( curlex=getchar()) == ' '); /*ПП игнорируются, до первого значащего символа*/
}

/*Функция обработки ошибки: возвращает в исходное состояние переменных*/
void error(void) {
    printf("\nОШИБКА!\n");
    while(getchar()!='\n');
    longjmp(begin,1);
}

/*Функция вычисляющаяя результат*/
int expr() {
    int e=add_sub(); /*Результат как возвращаемое значение функции сложения*/
    while (curlex == '+' || curlex == '-') /*пока есть символы сложения*/
        if (curlex == '+')
        {getlex(); e+=add_sub();} /*результат аддитивно растет*/
        else if (curlex == '-')
        {getlex(); e-=add_sub();} /*результат аддитивно убывает*/
    return e;
}
/*Функция сложения и вычитания*/
int add_sub() {
    /*Результат как возвращаемой значение функции умножения*/
    int a=mult_div();
    while (curlex == '*' || curlex == '/') /*пока есть символы умножения*/
        if (curlex == '*')
        {getlex(); a*=mult_div();} /*Результат мультипликативно растет*/
        else if (curlex == '/')
        {getlex(); a/=mult_div();} /*Результат мультипликативно убывает*/
    return a;
}

/*Функция умножения и деления*/
int mult_div() {
/*Результат как возвращаемой значение функции возведения в степень*/
    int a=power();
    while (curlex == '^') /*пока есть символы возведения в степень*/
        {getlex(); for(int k=0;k<power(); k++, a*=power());} /*Результат экспоненциально растет*/
    return a;
}

/*Функция возведения в степень*/
int power() {
    int m; /*Инициализация переменной, хранящей результат*/
    switch(curlex){ /*Интерпретация считанного символа 0-9*/
    case '0': 
    case '1': 
    case '2': 
    case '3': 
    case '4': 
    case '5':
    case '6': 
    case '7': 
    case '8': 
    case '9': m= curlex-'0'; break;
    case '(': getlex(); m=expr(); /*Рекурсивный вызов вычисления выражения в скобках*/
        if ( curlex == ')') break; /*Закрытие скобок*/
    default : error(); /*Вызов функции обработки ошибки при */
    }
    
getlex();
return m;
}

Код делает всё, кроме возведения в степень. Не могу отследить ошибку. Кроме того чувствую, что не учел правоассоциативность возведения в степень. То есть у меня 3^1^2 даст 9, а должно дать 3.

Помогите, пожалуйста.


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

Автор решения: Stepan Sokol

Хочешь найти ответ на сложную задачу - найди его сам.

Код решения:

#include <stdio.h>
#include <setjmp.h>

jmp_buf begin;
char curlex;

void getlex(void); 
int expr(void); 
int add_sub(void); 
int mult_div(void); 
int power(void);
void error(); 

int main() {
    int result; /*Переменная результата вычисления*/
    setjmp(begin); /*сохранения всех переменных окружения*/
    printf("==>"); 
    getlex();      /*Вызов функции чтения символов ввода: 
                     получает первый значащий символ в переменную curlex*/
    result=expr(); /*Вычисление результата с помощью функции*/
    if ( curlex != '\n') error(); /*Выброс ошибки и повтор ввода выражения
                                   если в потоке попались не 0-9, символы арифметики
                                   скобки или перевод*/
    printf("\n%d\n",result);
    return 0;
}
/*Функция принимает поток символов и записывает их в глобальную переменную*/
void getlex() {
while ( ( curlex=getchar()) == ' '); /*ПП игнорируются, до первого значащего символа*/
}

/*Функция обработки ошибки: возвращает в исходное состояние переменных*/
void error(void) {
    printf("\nОШИБКА!\n");
    while(getchar()!='\n');
    longjmp(begin,1);
}

/*Функция вычисляющаяя результат*/
int expr() {
    int e=add_sub(); /*Результат как возвращаемое значение функции сложения*/
    while (curlex == '+' || curlex == '-') /*пока есть символы сложения*/
        if (curlex == '+')
        {getlex(); e+=add_sub();} /*результат аддитивно растет*/
        else if (curlex == '-')
        {getlex(); e-=add_sub();} /*результат аддитивно убывает*/
    return e;
}
/*Функция сложения и вычитания*/
int add_sub() {
    /*Результат как возвращаемой значение функции умножения*/
    int a=mult_div();
    while (curlex == '*' || curlex == '/') /*пока есть символы умножения*/
        if (curlex == '*')
        {getlex(); a*=mult_div();} /*Результат мультипликативно растет*/
        else if (curlex == '/')
        {getlex(); a/=mult_div();} /*Результат мультипликативно убывает*/
    return a;
}

/*Функция умножения и деления*/
int mult_div() {
/*Результат как возвращаемой значение функции возведения в степень*/
    int a=power(), res=1;
    while (curlex == '^') /*пока есть символы возведения в степень*/
        {getlex(); 
        int m=mult_div();
        for(int k=0 ;k<m; k++, res*=a); 
        return res;} /*Результат экспоненциально растет*/
    return a;
}


/*Функция возведения в степень*/
int power() {
    int m; /*Инициализация переменной, хранящей результат*/

    switch(curlex){ /*Интерпретация считанного символа 0-9*/
    case '0': 
    case '1': 
    case '2': 
    case '3': 
    case '4': 
    case '5':
    case '6': 
    case '7': 
    case '8': 
    case '9': m= curlex-'0'; break;
    case '(': getlex(); m=expr(); /*Рекурсивный вызов вычисления выражения в скобках*/
        if ( curlex == ')') break; /*Закрытие скобок*/
    default : error(); /*Вызов функции обработки ошибки при */
    }
    
getlex();
return m;
}

Возведение в степень реализовано в виде еще одного уровня вложенности функции в порядке приоритета операций expr -> add_sub -> mult_div ->power(анализирует символы строки)

Правая ассоциативность обеспечивается рекурсией на уровне функции mult_div()

→ Ссылка