Эта программа токенизирует арифметическое выражение, указанное в инфиксной нотации. Это один из моих личных проектов, и я хотел бы получить совет специалиста.
После компиляции вы можете запустить программу с одним аргументом командной строки:
$ tokenize <arith_expr>
Это пример запуска программы:
$ tokenize "3^2 + 4.5 * (-2 - 1)"
Токенизатор в настоящее время поддерживает эти операторы. + - * / ^
.
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Can store an operator or an operand */
struct token {
char operator;
float operand;
};
/* 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;
}
/* 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 (!token) {
printf("Error: memory allocation failed");
exit(EXIT_FAILURE);
}
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 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);
if (token->operator == ' ') // token is operand
printf("%fn", token->operand);
else
printf("%cn", token->operator);
}
return EXIT_SUCCESS;
}
1 ответ
Ваш is_operator
функцию можно переписать как
return token == '+' || token == '-' || token == '*' || token == "https://codereview.stackexchange.com/" || token == '^';
На мой взгляд, намного чище.
В программе утечка памяти. Ваш tokenize
функция выделяет struct token
в куче, но объект никогда не освобождается.
Еще лучше, вместо того, чтобы возвращать struct token*
, просто верни struct token
. Структура определенно недостаточно велика, чтобы гарантировать выделение памяти в куче.
Вместо того, чтобы проверять, token
является операндом путем сравнения operator
к 0
используйте перечисление.
typedef enum {
TokenType_Operator,
TokenType_Operand
} token_type;
Тогда твой token
структура может состоять из token_type
перечисление и объединение.
struct token
{
token_type type;
union {
char operator;
float operand;
} data;
};
Если вы используете C11, вам даже не нужно ничего называть объединению.
В качестве следующего шага попробуйте реализовать токенизатор, который выполняет токенизацию за один проход. Прямо сейчас ваш токенизатор 1) удаляет пробелы (что включает линейный проход) и 2) копии остальной части строки после токенизации операнда в исходный буфер. Если у вас большой буфер, это может стать узким местом.
Распространенный способ реализации токенизатора — использование DFA (детерминированных конечных автоматов). Фактически, DFA — это отправная точка для большинства рукописных лексеров.
Токенизатор является частью запрограммированного мною калькулятора, и упомянутые вами баллы очень полезны, спасибо!
— Энди Суковски-Банг