Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions homework_5/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ set(homeworkName "${PROJECT_NAME}")
add_subdirectory(stack)

add_subdirectory(task_1)
add_subdirectory(task_2)
2 changes: 2 additions & 0 deletions homework_5/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Homework 5

[Task 1. Advanced brackets balance](/homework_5/task_1)

[Task 2. Postfix calculator](/homework_5/task_2)
11 changes: 11 additions & 0 deletions homework_5/task_2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
project("${homeworkName}_task_2")

add_library(postfixCalc postfixCalc.c)

add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} postfixCalc)
target_link_libraries(${PROJECT_NAME} stack)

add_executable(${PROJECT_NAME}_test test.c)
target_link_libraries(${PROJECT_NAME}_test postfixCalc)
target_link_libraries(${PROJECT_NAME}_test stack)
72 changes: 72 additions & 0 deletions homework_5/task_2/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <stdbool.h>
#include <stdio.h>

#include "postfixCalc.h"

bool readAndEvaluate(PostfixCalc *calc) {
printf("input digits and operands (allowed characters: 0123456789+-*/) (you can use space to split them):\n");
printf(" > ");

while (true) {
char input = getchar();
if (input == EOF || input == '\n') {
break;
}

if (input == ' ') {
continue;
}

switch (calcConsumeInput(calc, input))
{
case CALC_CONSUME_OK:
break;
case CALC_CONSUME_ALLOCATION_ERROR:
printf("allocation error\n");
return false;

case CALC_CONSUME_NOT_ENOUGH_OPERANDS:
printf("not enough operands to perform operation '%c'\n", input);
return false;

case CALC_CONSUME_UNKNOWN_INPUT:
printf("unknown character: '%c'\n", input);
return false;
}
}

int result = 0;

switch (calcTryGetResult(calc, &result))
{
case CALC_GET_RESULT_OK:
printf("evaluation result: %d\n", result);
break;

case CALC_IS_EMPTY_BEFORE_READ:
printf("nothing to evaluate\n");
return false;

case CALC_NOT_EMPTY_AFTER_READ:
printf("not all numbers are used\n");
return false;
}

return true;
}

int main(void) {
PostfixCalc *calc;

if (!calcCreate(&calc)) {
printf("allocation failed");
return 1;
}

if (!readAndEvaluate(calc)) {
calcDispose(calc);
return 1;
}

calcDispose(calc);
}
91 changes: 91 additions & 0 deletions homework_5/task_2/postfixCalc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "postfixCalc.h"

#include <stdbool.h>
#include <stdlib.h>

#include "../stack/stack.h"

typedef struct PostfixCalc {
Stack *stack;
} PostfixCalc;

bool calcCreate(PostfixCalc **calc) {
*calc = malloc(sizeof(PostfixCalc));
if (*calc == NULL) {
return false;
}

if (!stackCreate(&(*calc)->stack)) {
return false;
}

return true;
}

CalcConsumeResult calcConsumeInput(PostfixCalc *calc, char input) {
switch (input)
{
case '+':
case '-':
case '*':
case '/':
uint64_t valueA = -1;
if (!stackTryPop(calc->stack, &valueA)) {
return CALC_CONSUME_NOT_ENOUGH_OPERANDS;
}

uint64_t valueB = -1;
if (!stackTryPop(calc->stack, &valueB)) {
return CALC_CONSUME_NOT_ENOUGH_OPERANDS;
}

int left = (int)valueB;
int right = (int)valueA;

int value = 0;
if (input == '+') {
value = left + right;
} else if (input == '-') {
value = left - right;
} else if (input == '*') {
value = left * right;
} else if (input == '/') {
value = left / right;
}

if (!stackPush(calc->stack, (uint64_t)value)) {
return CALC_CONSUME_ALLOCATION_ERROR;
}

break;

default:
if (input >= '0' && input <= '9') {
uint64_t value = input - '0';
if (!stackPush(calc->stack, value)) {
return CALC_CONSUME_ALLOCATION_ERROR;
}
} else {
return CALC_CONSUME_UNKNOWN_INPUT;
}

break;
}

return CALC_CONSUME_OK;
}

CalcGetResultResult calcTryGetResult(PostfixCalc *calc, int *result) {
uint64_t value = 0;
if (!stackTryPop(calc->stack, &value)) {
return CALC_IS_EMPTY_BEFORE_READ;
}

*result = (int)value;
return stackIsEmpty(calc->stack) ? CALC_GET_RESULT_OK : CALC_NOT_EMPTY_AFTER_READ;
}

void calcDispose(PostfixCalc *calc) {
stackDispose(calc->stack);
free(calc);
}
55 changes: 55 additions & 0 deletions homework_5/task_2/postfixCalc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#pragma once

#include <stdbool.h>

/// @brief Calculator that works with basic operations like
/// addition, subtraction, multiplication and division
/// using integer numbers.
///
/// Recieves expression character by character in
/// postfix notation(also known as Reverse Polish notation(PRN))
typedef struct PostfixCalc PostfixCalc;

typedef enum {
CALC_CONSUME_OK,
CALC_CONSUME_NOT_ENOUGH_OPERANDS,
CALC_CONSUME_ALLOCATION_ERROR,
CALC_CONSUME_UNKNOWN_INPUT
} CalcConsumeResult;

typedef enum {
CALC_GET_RESULT_OK,
CALC_IS_EMPTY_BEFORE_READ,
CALC_NOT_EMPTY_AFTER_READ
} CalcGetResultResult;

/// @brief Creates `PostfixCalc`
/// @param calc Pointer to store created `PostfixCalc` to
/// @return `true` if successful, `false` otherwise (allocation failed)
bool calcCreate(PostfixCalc **calc);

/// @brief Tries to read input to calculator
/// @param calc Pointer to `PostfixCalc` instance
/// @param input Input to read
/// @return `CALC_CONSUME_OK` if read successfully
///
/// `CALC_CONSUME_NOT_ENOUGH_OPERANDS` if there were not enough operands provided before specified operator
///
/// `CALC_CONSUME_UNKNOWN_INPUT` if unknown character was provided
///
/// `CALC_CONSUME_ALLOCATION_ERROR` if allocation error ocurred internally
CalcConsumeResult calcConsumeInput(PostfixCalc *calc, char input);

/// @brief Tries to get last result
/// @param calc Pointer to `PostfixCalc` instance
/// @param result Pointer to write evaluation result to
/// @return `CALC_GET_RESULT_OK` if evaluated successfully
///
/// `CALC_IS_EMPTY_BEFORE_READ` if there wasn't any result to return
///
/// `CALC_NOT_EMPTY_AFTER_READ` if there are more than one result
CalcGetResultResult calcTryGetResult(PostfixCalc *calc, int *result);

/// @brief Disposes calculator
/// @param calc `PostfixCalc` to dispose
void calcDispose(PostfixCalc *calc);
102 changes: 102 additions & 0 deletions homework_5/task_2/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#define CTEST_MAIN
#define CTEST_SEGFAULT
#include "../../ctest/ctest.h"

#include "postfixCalc.h"

int main(int argc, const char *argv[]) {
return ctest_main(argc, argv);
}

PostfixCalc *createCalc(void) {
PostfixCalc *calc;
ASSERT_TRUE(calcCreate(&calc));
ASSERT_NOT_NULL(calc);

return calc;
}

void assertResult(PostfixCalc *calc, int expected) {
int result = 0;
ASSERT_EQUAL(calcTryGetResult(calc, &result), CALC_GET_RESULT_OK);
ASSERT_EQUAL(expected, result);
}

void assertConsumeString(PostfixCalc *calc, const char *input, int expected) {
for (int i = 0; input[i] != '\0'; ++i) {
ASSERT_EQUAL(calcConsumeInput(calc, input[i]), CALC_CONSUME_OK);
}

assertResult(calc, expected);
}

void testCalc(const char *input, int expected) {
PostfixCalc *calc = createCalc();

assertConsumeString(calc, input, expected);

calcDispose(calc);
}

CTEST(postfixCalcTests, createTest) {
PostfixCalc *calc = createCalc();
calcDispose(calc);
}

CTEST(postfixCalcTests, singleNumberTest) {
testCalc("4", 4);
}

CTEST(postfixCalcTests, additionTest) {
testCalc("12+", 3);
testCalc("00+", 0);
testCalc("50+", 5);
testCalc("09+", 9);
testCalc("78+", 15);
testCalc("99+", 18);
}

CTEST(postfixCalcTests, subtractionTest) {
testCalc("53-", 2);
testCalc("00-", 0);
testCalc("50-", 5);
testCalc("09-", -9);
testCalc("78-", -1);
testCalc("99-", 0);
testCalc("98-", 1);
testCalc("38-", -5);
}

CTEST(postfixCalcTests, multiplicationTest) {
testCalc("53*", 15);
testCalc("00*", 0);
testCalc("50*", 0);
testCalc("09*", 0);
testCalc("78*", 56);
testCalc("99*", 81);
testCalc("98*", 72);
testCalc("38*", 24);
testCalc("88*", 64);
}

CTEST(postfixCalcTests, divisionTest) {
testCalc("51/", 5);
testCalc("09/", 0);
testCalc("78/", 0);
testCalc("99/", 1);
testCalc("93/", 3);
testCalc("31/", 3);
testCalc("88/", 1);
testCalc("82/", 4);
testCalc("62/", 3);
testCalc("63/", 2);
}

CTEST(postfixCalcTests, complexTest) {
testCalc("12+34++", 10);
testCalc("12+34+*", 21);
testCalc("12+34+*2*", 42);
testCalc("12*34*56***", 720);

testCalc("96-12+*", 9);
}