Калькулятор на языке C — Анализ арифметического выражения с использованием алгоритма маневрового двора

Это простой арифметический калькулятор, который анализирует математические выражения, указанные в инфиксной нотации, с использованием маневровый алгоритм. Это один из моих личных проектов, и я хотел бы получить совет специалиста.

После компиляции вы можете запустить программу с одним аргументом командной строки:

$ calc <arith_expr>

Это пример запуска калькулятора:

$ calc "3^2 + 4 * (2 - 1)"

Переданное арифметическое выражение маркируется, а операнды и операторы хранятся в двух разных стеках, реализованных в виде связанных списков. Калькулятор в настоящее время поддерживает эти операторы + - * / ^.

В calc.c У меня есть:

#define _POSIX_C_SOURCE 199309L
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stack.h"

struct node_float *operands = NULL;
struct node_char *operators = NULL;

/* Can store an operator or an operand */
struct token {
    char operator;
    float operand;
};

/* Return precedence of a given operator */
int prec(char op)
{
    switch (op) {
    case '+':
    case '-':
        return 2;
    case '*':
    case "https://codereview.stackexchange.com/":
        return 3;
    case '^':
        return 4;
    default:
        printf("Invalid operator: %cn", op);
        exit(EXIT_FAILURE);
    }
}

/* Check if given token is an operator */
bool is_operator(char token)
{
    if (token == '+' ||
        token == '-' ||
        token == '*' ||
        token == "https://codereview.stackexchange.com/" ||
        token == '^')
        return true;
    else
        return false;
}

/* Apply mathematical operation to top two elements on the stack */
float eval(char op)
{
    float tmp = pop_float(&operands);
    switch (op) {
    case '+':
        return pop_float(&operands) + tmp;
    case '-':
        return pop_float(&operands) - tmp;
    case '*':
        return pop_float(&operands) * tmp;
    case "https://codereview.stackexchange.com/":
        return pop_float(&operands) / tmp;
    case '^':
        return pow(pop_float(&operands), tmp);
    default:
        printf("Invalid operator: %cn", op);
        exit(EXIT_FAILURE);
    }
}

/* Remove all spaces from string */
void rmspaces(char *str)
{
    const char *dup = str;
    do {
        while (isspace(*dup))
            ++dup;
    } while (*str++ = *dup++);
}

/* Return first token of given arithmetic expression */
struct token *tokenize(char **expr)
{
    static bool op_allowed = false;
    struct token *token = malloc(sizeof *token);

    if (op_allowed && is_operator(*expr[0])) {
        token->operator = *expr[0];
        ++(*expr);
        op_allowed = false;
    } else if (!op_allowed && *expr[0] == '(') {
        token->operator = *expr[0];
        ++(*expr);
    } else if (op_allowed && *expr[0] == ')') {
        token->operator = *expr[0];
        ++(*expr);
    } else {
        char *rest;
        token->operand = strtof(*expr, &rest);
        token->operator="";
        if (*expr == rest) {
            printf("Invalid expressionn");
            exit(EXIT_FAILURE);
        }
        strcpy(*expr, rest);
        op_allowed = true;
    }

    return token;
}

/* Handle a given token, which might be an operand or an operator */
void handle_token(struct token *token)
{
    if (token->operator == '') { // token is operand
        push_float(&operands, token->operand);
    } else if (is_operator(token->operator)) {
        while (operators != NULL && operators->data != '(' &&
            prec(token->operator) <= prec(operators->data)) {
            float result = eval(pop_char(&operators));
            push_float(&operands, result);
        }
        push_char(&operators, token->operator);
    } else if (token->operator == '(') {
        push_char(&operators, token->operator);
    } else if (token->operator == ')') {
        while (operators != NULL && operators->data != '(') {
            float result = eval(pop_char(&operators));
            push_float(&operands, result);
        }
        pop_char(&operators);
    } else {
        printf("Invalid operator: %cn", token->operator);
        exit(EXIT_FAILURE);
    }
}

/* Handle command line arguments */
int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("Usage:   %s <arith_expr>n"
            "Example: %s "5 2 3 * +"n",
            argv[0], argv[0]);
        exit(EXIT_FAILURE);
    }

    char *expr = argv[1];
    rmspaces(expr);
    struct token *token;
    while (expr[0] != '') {
        token = tokenize(&expr);
        handle_token(token);
    }
    while (operators != NULL) {
        float result = eval(pop_char(&operators));
        push_float(&operands, result);
    }
    if (operands == NULL || operands->next != NULL) {
        printf("Too many operands on stackn");
        exit(EXIT_FAILURE);
    }

    printf("Result: %fn", operands->data);

    return 0;
}

В stack.c У меня есть:

#include <stdio.h>
#include <stdlib.h>
#include "stack.h"

/* Push float onto stack */
void push_float(struct node_float **head, float data)
{
    struct node_float *new = malloc(sizeof *new);
    new->data = data;
    new->next = *head;
    *head = new;
}

/* Pop float from stack */
float pop_float(struct node_float **head)
{
    if (*head == NULL) {
        printf("Error: stack underflown");
        exit(EXIT_FAILURE);
    }
    struct node_float *tmp = *head;
    float data = tmp->data;
    *head = tmp->next;
    free(tmp);
    return data;
}

/* Push char onto stack */
void push_char(struct node_char **head, char data)
{
    struct node_char *new = malloc(sizeof *new);
    new->data = data;
    new->next = *head;
    *head = new;
}

/* Pop char from stack */
char pop_char(struct node_char **head)
{
    if (*head == NULL) {
        printf("Error: stack underflown");
        exit(EXIT_FAILURE);
    }
    struct node_char *tmp = *head;
    char data = tmp->data;
    *head = tmp->next;
    free(tmp);
    return data;
}

В stack.h У меня есть:

#ifndef STACK_H
#define STACK_H

struct node_float {
    float data;
    struct node_float *next;
};

struct node_char {
    char data;
    struct node_char *next;
};

/* Push float onto stack */
void push_float(struct node_float **head, float data);

/* Pop float from stack */
float pop_float(struct node_float **head);

/* Push char onto stack */
void push_char(struct node_char **head, char data);

/* Pop char from stack */
char pop_char(struct node_char **head);

#endif /* STACK_H */

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *