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 @@ -6,3 +6,4 @@ add_subdirectory(stack)

add_subdirectory(task_1)
add_subdirectory(task_2)
add_subdirectory(task_3)
2 changes: 2 additions & 0 deletions homework_5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
[Task 1. Advanced brackets balance](/homework_5/task_1)

[Task 2. Postfix calculator](/homework_5/task_2)

[Task 3. Shunting yard](/homework_5/task_3)
11 changes: 11 additions & 0 deletions homework_5/task_3/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
72 changes: 72 additions & 0 deletions homework_5/task_3/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <stdbool.h>
#include <stdio.h>

#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);
}
129 changes: 129 additions & 0 deletions homework_5/task_3/shuntingYard.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include "shuntingYard.h"

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

#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);
}
41 changes: 41 additions & 0 deletions homework_5/task_3/shuntingYard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <stdbool.h>

/// @brief Structure for converting infix notation to postfix (also known as Reverse Polish notation (PRN))
typedef struct ShuntingYard ShuntingYard;

typedef enum {
YARD_OK,
YARD_UNKNOWN_INPUT,
YARD_INCORRECT_INPUT,
YARD_BUFFER_OVERFLOW,
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 (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
/// @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);
100 changes: 100 additions & 0 deletions homework_5/task_3/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#define CTEST_MAIN
#define CTEST_SEGFAULT
#include "../../ctest/ctest.h"

#include <stdint.h>

#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);
shuntingYardDispose(yard);
}

CTEST(yardTests, createTest) {
ShuntingYard *yard = createYard();
shuntingYardDispose(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/+");
}