Это простой арифметический калькулятор, который анализирует математические выражения, указанные в инфиксной нотации, с использованием маневровый алгоритм. Это один из моих личных проектов, и я хотел бы получить совет специалиста.
После компиляции вы можете запустить программу с одним аргументом командной строки:
$ 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 */