From 3796939272b779a539bbc4249072593e711cead1 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:54:48 +0300 Subject: [PATCH 1/7] Homework 5 - task 3 --- homework_5/CMakeLists.txt | 2 + homework_5/task_3/CMakeLists.txt | 11 +++ homework_5/task_3/main.c | 71 +++++++++++++++++ homework_5/task_3/shuntingYard.c | 128 +++++++++++++++++++++++++++++++ homework_5/task_3/shuntingYard.h | 19 +++++ homework_5/task_3/test.c | 100 ++++++++++++++++++++++++ 6 files changed, 331 insertions(+) create mode 100644 homework_5/task_3/CMakeLists.txt create mode 100644 homework_5/task_3/main.c create mode 100644 homework_5/task_3/shuntingYard.c create mode 100644 homework_5/task_3/shuntingYard.h create mode 100644 homework_5/task_3/test.c diff --git a/homework_5/CMakeLists.txt b/homework_5/CMakeLists.txt index dc70726..e0368e6 100644 --- a/homework_5/CMakeLists.txt +++ b/homework_5/CMakeLists.txt @@ -3,3 +3,5 @@ project(homework_5) set(homeworkName "${PROJECT_NAME}") add_subdirectory(stack) + +add_subdirectory(task_3) diff --git a/homework_5/task_3/CMakeLists.txt b/homework_5/task_3/CMakeLists.txt new file mode 100644 index 0000000..2d1fc3b --- /dev/null +++ b/homework_5/task_3/CMakeLists.txt @@ -0,0 +1,11 @@ +project("${homeworkName}_task_3") + +add_library(shuntingYard shuntingYard.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} shuntingYard) +target_link_libraries(${PROJECT_NAME} stack) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test shuntingYard) +target_link_libraries(${PROJECT_NAME}_test stack) diff --git a/homework_5/task_3/main.c b/homework_5/task_3/main.c new file mode 100644 index 0000000..a20eccb --- /dev/null +++ b/homework_5/task_3/main.c @@ -0,0 +1,71 @@ +#include + +#include "shuntingYard.h" + +bool consumeInput(ShuntingYard *yard, char input) { + char buffer[1024] = { 0 }; + int safeBufferLength = 1024 - 1; + int bufferPosition = 0; + + switch (shuntingYardConsumeInput(yard, input, buffer, safeBufferLength, &bufferPosition)) + { + case YARD_OK: + break; + + case YARD_ALLOCATION_ERROR: + printf("allocation error\n"); + return false; + + case YARD_INCORRECT_INPUT: + printf("incorrect input - probably missed an opening parenthesis\n"); + return false; + + case YARD_BUFFER_OVERFLOW: + printf("buffer ovwerflow - input was too long\n"); + return false; + + case YARD_UNKNOWN_INPUT: + printf("unknown character: '%c'\n", input); + return false; + } + + printf("%s", buffer); + return true; +} + +bool readAndEvaluate(ShuntingYard *yard) { + printf("input infix notation expression (allowed characters: ()0123456789+-*/) (you can use space to split characters):\n"); + printf(" > "); + + while (true) { + char input = getchar(); + if (input == EOF || input == '\n') { + break; + } + + if (input == ' ') { + continue; + } + + if (!consumeInput(yard, input)) { + return false; + } + } + return consumeInput(yard, '\0'); +} + +int main(void) { + ShuntingYard *yard; + + if (!shuntingYardCreate(&yard)) { + printf("allocation failed"); + return 1; + } + + if (!readAndEvaluate(yard)) { + shuntingYardDispose(yard); + return 1; + } + + shuntingYardDispose(yard); +} diff --git a/homework_5/task_3/shuntingYard.c b/homework_5/task_3/shuntingYard.c new file mode 100644 index 0000000..c716290 --- /dev/null +++ b/homework_5/task_3/shuntingYard.c @@ -0,0 +1,128 @@ +#include "shuntingYard.h" + +#include +#include + +#include "../stack/stack.h" + +typedef struct ShuntingYard { + Stack *stack; +} ShuntingYard; + +bool shuntingYardCreate(ShuntingYard **yard) { + *yard = malloc(sizeof(ShuntingYard)); + if (*yard == NULL) { + return false; + } + + if (!stackCreate(&(*yard)->stack)) { + return false; + } + + return true; +} + +static int getPrecedence(char operator) { + switch (operator) + { + case '+': + case '-': + return 1; + + case '*': + case '/': + return 2; + + default: + return -1; + } +} + +bool writeToBuffer(char output, char *buffer, int bufferLength, int *bufferPosition) { + buffer[*bufferPosition] = output; + ++*bufferPosition; + return *bufferPosition < bufferLength; +} + +YardConsumeResult shuntingYardConsumeInput(ShuntingYard *yard, char input, char *outputBuffer, int bufferLength, int *bufferPosition) { + if (*bufferPosition >= bufferLength) { + return YARD_BUFFER_OVERFLOW; + } + + switch (input) + { + case '(': + if (!stackPush(yard->stack, input)) { + return YARD_ALLOCATION_ERROR; + } + return YARD_OK; + + case '+': + case '-': + case '*': + case '/': + { + uint64_t stackTop = 0; + while (stackTryPeek(yard->stack, &stackTop)) { + char chr = (char)stackTop; + + if (getPrecedence(chr) < getPrecedence(input)) { + break; + } + + stackTryPop(yard->stack, &stackTop); + if (!writeToBuffer(chr, outputBuffer, bufferLength, bufferPosition)) { + return YARD_BUFFER_OVERFLOW; + } + } + + if (!stackPush(yard->stack, input)) { + return YARD_ALLOCATION_ERROR; + } + return YARD_OK; + } + + case ')': + { + uint64_t stackTop = 0; + while (stackTryPop(yard->stack, &stackTop)) { + char chr = (char)stackTop; + + if (chr == '(') { + return YARD_OK; + } + if (!writeToBuffer(chr, outputBuffer, bufferLength, bufferPosition)) { + return YARD_BUFFER_OVERFLOW; + } + } + + return YARD_INCORRECT_INPUT; + } + + case '\0': + { + uint64_t stackTop = 0; + while (stackTryPop(yard->stack, &stackTop)) { + if (!writeToBuffer((char)stackTop, outputBuffer, bufferLength, bufferPosition)) { + return YARD_BUFFER_OVERFLOW; + } + } + + return YARD_OK; + } + + default: + if (input >= '0' && input <= '9') { + if (!writeToBuffer(input, outputBuffer, bufferLength, bufferPosition)) { + return YARD_BUFFER_OVERFLOW; + } + return YARD_OK; + } + return YARD_UNKNOWN_INPUT; + } +} + +void shuntingYardDispose(ShuntingYard *yard) { + stackDispose(yard->stack); + free(yard); +} diff --git a/homework_5/task_3/shuntingYard.h b/homework_5/task_3/shuntingYard.h new file mode 100644 index 0000000..8a1f04c --- /dev/null +++ b/homework_5/task_3/shuntingYard.h @@ -0,0 +1,19 @@ +#pragma once + +typedef struct ShuntingYard ShuntingYard; + +typedef enum { + YARD_OK, + YARD_UNKNOWN_INPUT, + YARD_INCORRECT_INPUT, + YARD_BUFFER_OVERFLOW, + YARD_ALLOCATION_ERROR +} YardConsumeResult; + +typedef void (*OutputFunction)(char output); + +bool shuntingYardCreate(ShuntingYard **yard); + +YardConsumeResult shuntingYardConsumeInput(ShuntingYard *yard, char input, char *outputBuffer, int bufferLength, int *bufferPosition); + +void shuntingYardDispose(ShuntingYard *yard); diff --git a/homework_5/task_3/test.c b/homework_5/task_3/test.c new file mode 100644 index 0000000..084eddc --- /dev/null +++ b/homework_5/task_3/test.c @@ -0,0 +1,100 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "../stack/stack.h" +#include "shuntingYard.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +ShuntingYard *createYard(void) { + ShuntingYard *yard; + ASSERT_TRUE(shuntingYardCreate(&yard)); + ASSERT_NOT_NULL(yard); + + return yard; +} + +void assertOutput(ShuntingYard *yard, const char *input, const char *expectedOutput) { +#define BUFFER_LENGTH 1024 + char buffer[BUFFER_LENGTH] = { 0 }; + int bufferPosition = 0; + + for (int i = 0; input[i] != '\0'; ++i) { + ASSERT_EQUAL(shuntingYardConsumeInput(yard, input[i], buffer, BUFFER_LENGTH, &bufferPosition), YARD_OK); + } + + ASSERT_EQUAL(shuntingYardConsumeInput(yard, '\0', buffer, BUFFER_LENGTH, &bufferPosition), YARD_OK); + + ASSERT_STR(buffer, expectedOutput); + +#undef BUFFER_LENGTH +} + +void testYard(const char *input, const char *expectedOutput) { + ShuntingYard *yard = createYard(); + assertOutput(yard, input, expectedOutput); + free(yard); +} + +CTEST(yardTests, createTest) { + ShuntingYard *yard = createYard(); + free(yard); +} + +CTEST(yardTests, singleNumberTest) { + testYard("0", "0"); + testYard("5", "5"); +} + +CTEST(yardTests, simpleOperationTest) { + testYard("0+0", "00+"); + testYard("5+5", "55+"); + + testYard("5-7", "57-"); + testYard("5-5", "55-"); + + testYard("0*5", "05*"); + testYard("8*4", "84*"); + + testYard("4/2", "42/"); + testYard("9/4", "94/"); +} + +CTEST(yardTests, simpleOperationInParenthesesTest) { + testYard("(0+0)", "00+"); + testYard("(5+5)", "55+"); + + testYard("(5-7)", "57-"); + testYard("(5-5)", "55-"); + + testYard("(0*5)", "05*"); + testYard("(8*4)", "84*"); + + testYard("(4/2)", "42/"); + testYard("(9/4)", "94/"); +} + +CTEST(yardTests, operationBetweenParenthesesAndNumberTest) { + testYard("(0+0)*4", "00+4*"); + testYard("(5+5)-3", "55+3-"); + + testYard("(5-7)/1", "57-1/"); + testYard("1+(5-5)", "155-+"); + + testYard("(0*5)*9", "05*9*"); + testYard("2+(8*4)", "284*+"); + + testYard("(4/2)-8", "42/8-"); + testYard("(9/4)*6", "94/6*"); +} + +CTEST(yardTests, operationBetweenTwoParenthesesTest) { + testYard("(4-1)*(8+1)", "41-81+*"); + testYard("(9*2)/(9-2)", "92*92-/"); + testYard("(6/3)+(8/2)", "63/82/+"); +} From 9ce85c66aaf4a1a0c083191f2066956fc82073cd Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:34:53 +0300 Subject: [PATCH 2/7] Removed leftover function typedef --- homework_5/task_3/shuntingYard.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/homework_5/task_3/shuntingYard.h b/homework_5/task_3/shuntingYard.h index 8a1f04c..6dbd7f8 100644 --- a/homework_5/task_3/shuntingYard.h +++ b/homework_5/task_3/shuntingYard.h @@ -10,8 +10,6 @@ typedef enum { YARD_ALLOCATION_ERROR } YardConsumeResult; -typedef void (*OutputFunction)(char output); - bool shuntingYardCreate(ShuntingYard **yard); YardConsumeResult shuntingYardConsumeInput(ShuntingYard *yard, char input, char *outputBuffer, int bufferLength, int *bufferPosition); From 1e192a5a265c9497762bf18e83931163ea888ded Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:42:10 +0300 Subject: [PATCH 3/7] Added comments (hw5 task 3) --- homework_5/task_3/shuntingYard.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/homework_5/task_3/shuntingYard.h b/homework_5/task_3/shuntingYard.h index 6dbd7f8..eef6ab2 100644 --- a/homework_5/task_3/shuntingYard.h +++ b/homework_5/task_3/shuntingYard.h @@ -1,5 +1,6 @@ #pragma once +/// @brief Structure for converting infix notation to postfix (also known as Reverse Polish notation (PRN)) typedef struct ShuntingYard ShuntingYard; typedef enum { @@ -10,8 +11,29 @@ typedef enum { YARD_ALLOCATION_ERROR } YardConsumeResult; +/// @brief Creates `ShuntingYard` +/// @param yard Pointer to store created `ShuntingYard` to +/// @return `true` if successful, `false` otherwise (allocation failed) bool shuntingYardCreate(ShuntingYard **yard); +/// @brief Tries to read input to shunting yard +/// @param yard Pointer to `ShuntingYard` instance +/// @param input Input to read +/// @param outputBuffer Buffer to write to +/// @param bufferLength Length of buffer; used to prevent buffer overflow +/// @param bufferPosition Position in buffer to write character into +/// @return `YARD_OK` if read successfully +/// +/// `YARD_UNKNOWN_INPUT` if unknown character was provided +/// +/// `YARD_INCORRECT_INPUT` if previously characters were provided in incorrect format, +/// probably an opening parenthesis was missed +/// +/// `YARD_BUFFER_OVERFLOW` if `bufferPosition` exceeded `bufferLength` +/// +/// `YARD_ALLOCATION_ERROR` if allocation error ocurred internally YardConsumeResult shuntingYardConsumeInput(ShuntingYard *yard, char input, char *outputBuffer, int bufferLength, int *bufferPosition); +/// @brief Disposes shunting yard +/// @param yard `ShuntingYard` to dispose void shuntingYardDispose(ShuntingYard *yard); From 825bff9d958aeb10379a8ea2a5df3744afe994da Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:05:54 +0300 Subject: [PATCH 4/7] Dispose shunting yard properly --- homework_5/task_3/test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homework_5/task_3/test.c b/homework_5/task_3/test.c index 084eddc..15ac2b2 100644 --- a/homework_5/task_3/test.c +++ b/homework_5/task_3/test.c @@ -38,12 +38,12 @@ void assertOutput(ShuntingYard *yard, const char *input, const char *expectedOut void testYard(const char *input, const char *expectedOutput) { ShuntingYard *yard = createYard(); assertOutput(yard, input, expectedOutput); - free(yard); + shuntingYardDispose(yard); } CTEST(yardTests, createTest) { ShuntingYard *yard = createYard(); - free(yard); + shuntingYardDispose(yard); } CTEST(yardTests, singleNumberTest) { From 96cf292e3ced435763df4b7040b329a64897a1a3 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:09:01 +0300 Subject: [PATCH 5/7] Added hint for end-of-input --- homework_5/task_3/shuntingYard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework_5/task_3/shuntingYard.h b/homework_5/task_3/shuntingYard.h index eef6ab2..3cc5c77 100644 --- a/homework_5/task_3/shuntingYard.h +++ b/homework_5/task_3/shuntingYard.h @@ -18,7 +18,7 @@ bool shuntingYardCreate(ShuntingYard **yard); /// @brief Tries to read input to shunting yard /// @param yard Pointer to `ShuntingYard` instance -/// @param input Input to read +/// @param input Input to read (use `'\0'` flush remaining characters to buffer) /// @param outputBuffer Buffer to write to /// @param bufferLength Length of buffer; used to prevent buffer overflow /// @param bufferPosition Position in buffer to write character into From 418656527d1dc9f083388be1d6d34d785f424f8d Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Tue, 10 Dec 2024 03:20:18 +0300 Subject: [PATCH 6/7] Added stdbool.h includes (hw5 task 3) --- homework_5/task_3/main.c | 1 + homework_5/task_3/shuntingYard.c | 1 + homework_5/task_3/shuntingYard.h | 2 ++ 3 files changed, 4 insertions(+) diff --git a/homework_5/task_3/main.c b/homework_5/task_3/main.c index a20eccb..2aea83e 100644 --- a/homework_5/task_3/main.c +++ b/homework_5/task_3/main.c @@ -1,3 +1,4 @@ +#include #include #include "shuntingYard.h" diff --git a/homework_5/task_3/shuntingYard.c b/homework_5/task_3/shuntingYard.c index c716290..3fc153e 100644 --- a/homework_5/task_3/shuntingYard.c +++ b/homework_5/task_3/shuntingYard.c @@ -1,5 +1,6 @@ #include "shuntingYard.h" +#include #include #include diff --git a/homework_5/task_3/shuntingYard.h b/homework_5/task_3/shuntingYard.h index 3cc5c77..ed7155b 100644 --- a/homework_5/task_3/shuntingYard.h +++ b/homework_5/task_3/shuntingYard.h @@ -1,5 +1,7 @@ #pragma once +#include + /// @brief Structure for converting infix notation to postfix (also known as Reverse Polish notation (PRN)) typedef struct ShuntingYard ShuntingYard; From 1def155215b0b1cd0f7374f6d2737c0a5bf547d3 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Sun, 22 Dec 2024 22:25:12 +0300 Subject: [PATCH 7/7] Added task name to README (hw5 task 3) --- homework_5/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homework_5/README.md b/homework_5/README.md index c6918c2..44877d4 100644 --- a/homework_5/README.md +++ b/homework_5/README.md @@ -1 +1,3 @@ # Homework 5 + +[Task 3. Shunting yard](/homework_5/task_3)