mirror of
https://github.com/sloven-c/calculator.git
synced 2025-11-04 14:17:23 +01:00
Fixed more footguns
This commit is contained in:
53
main.c
53
main.c
@@ -34,14 +34,23 @@ void validate_malloc(const void *p);
|
|||||||
void throw_error(char *msg);
|
void throw_error(char *msg);
|
||||||
|
|
||||||
int main(const int argc, char *argv[]) {
|
int main(const int argc, char *argv[]) {
|
||||||
|
/*
|
||||||
|
* TODO
|
||||||
|
* Implement calculating with negative numbers
|
||||||
|
* and if you're suicidal enough calculation with floats
|
||||||
|
*/
|
||||||
const string expression = get_expression(argc, argv);
|
const string expression = get_expression(argc, argv);
|
||||||
if (expression.string == nullptr) return 0;
|
if (expression.string == nullptr) {
|
||||||
|
fprintf(stderr, "Couldn't parse the expression\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
stack tokens = split_into_tokens(expression);
|
stack tokens = split_into_tokens(expression);
|
||||||
stack_print(&tokens, "tokens");
|
// stack_print(&tokens, "tokens");
|
||||||
|
|
||||||
stack output_queue = get_output_queue(&tokens);
|
stack output_queue = get_output_queue(&tokens);
|
||||||
|
|
||||||
|
// stack_print(&output_queue, "Output");
|
||||||
printf("%d\n", calculate_stack(&output_queue));
|
printf("%d\n", calculate_stack(&output_queue));
|
||||||
|
|
||||||
stack_deinit(&output_queue);
|
stack_deinit(&output_queue);
|
||||||
@@ -66,9 +75,11 @@ int calculate_stack(const stack *output_queue) {
|
|||||||
if (r == 1) throw_error("Failed to push number to calculation stack");
|
if (r == 1) throw_error("Failed to push number to calculation stack");
|
||||||
break;
|
break;
|
||||||
case Operator:
|
case Operator:
|
||||||
const stackData aP = stack_pop(&calculation_stack);
|
|
||||||
const stackData bP = stack_pop(&calculation_stack);
|
const stackData bP = stack_pop(&calculation_stack);
|
||||||
if (aP.ret_code == 1 || bP.ret_code == 1) throw_error("Failed to pop numbers from calculation stack");
|
const stackData aP = stack_pop(&calculation_stack);
|
||||||
|
if (aP.ret_code == 1 || bP.ret_code == 1)
|
||||||
|
throw_error(
|
||||||
|
"Failed to pop numbers from calculation stack");
|
||||||
|
|
||||||
const int result = calculate(aP.data.n, bP.data.n, element.data.string[0]);
|
const int result = calculate(aP.data.n, bP.data.n, element.data.string[0]);
|
||||||
const int rSp = stack_push(&calculation_stack, (stackInput){.n = result}, false);
|
const int rSp = stack_push(&calculation_stack, (stackInput){.n = result}, false);
|
||||||
@@ -76,7 +87,8 @@ int calculate_stack(const stack *output_queue) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case Invalid:
|
case Invalid:
|
||||||
fprintf(stderr, "The following string is not valid part of expression: '%s'\n", element.data.string);
|
fprintf(stderr, "The following string is not valid part of expression: '%s'\n",
|
||||||
|
element.data.string);
|
||||||
throw_error(nullptr);
|
throw_error(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,6 +180,9 @@ stack get_output_queue(const stack *tokenisedExpression) {
|
|||||||
// push number to output queue
|
// push number to output queue
|
||||||
stack_push(&output_stack, token.data, false);
|
stack_push(&output_stack, token.data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printf("pushing output stack, i = %lu\n", i);
|
||||||
|
// stack_print(&output_stack, "Output");
|
||||||
}
|
}
|
||||||
|
|
||||||
// pop everything from operator to output stack
|
// pop everything from operator to output stack
|
||||||
@@ -198,24 +213,22 @@ int push_operator(const char operatorToPush, stack *output_stack, stack *operato
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
stackData lastStackOp = stack_get(operator_stack, operator_stack->i);
|
||||||
for (stackData lastStackOp = stack_get(operator_stack, operator_stack->i);
|
while (lastStackOp.ret_code == 0 && stack_higher_precedence(operatorToPush, lastStackOp.data.ch)) {
|
||||||
stack_higher_precedence(operatorToPush, lastStackOp.data.ch);
|
|
||||||
lastStackOp = stack_get(operator_stack, operator_stack->i)) {
|
|
||||||
if (lastStackOp.ret_code == 1) return 1;
|
|
||||||
|
|
||||||
const stackData opToPush = stack_pop(operator_stack);
|
const stackData opToPush = stack_pop(operator_stack);
|
||||||
if (opToPush.ret_code == 1) return 1;
|
if (opToPush.ret_code == 1) return 1;
|
||||||
|
|
||||||
const int r = stack_push(output_stack, opToPush.data, true);
|
const int r = stack_push(output_stack, opToPush.data, true);
|
||||||
if (r == 1) return 1;
|
if (r == 1) return 1;
|
||||||
|
lastStackOp = stack_get(operator_stack, operator_stack->i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack_push(operator_stack, (stackInput){.ch = operatorToPush}, true);
|
return stack_push(operator_stack, (stackInput){.ch = operatorToPush}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool stack_higher_precedence(const char op, const char stackOp) {
|
bool stack_higher_precedence(const char op, const char stackOp) {
|
||||||
const int stackOpPrecedence = validate_char(stackOp).opPrecedence, opPrecedence = validate_char(op).opPrecedence;
|
const int stackOpPrecedence = validate_char(stackOp).opPrecedence, opPrecedence = validate_char(op).
|
||||||
|
opPrecedence;
|
||||||
if (stackOpPrecedence == -1 || opPrecedence == -1) return false;
|
if (stackOpPrecedence == -1 || opPrecedence == -1) return false;
|
||||||
|
|
||||||
return stackOpPrecedence > opPrecedence;
|
return stackOpPrecedence > opPrecedence;
|
||||||
@@ -226,8 +239,11 @@ stack split_into_tokens(const string expression) {
|
|||||||
int lastOp = -1;
|
int lastOp = -1;
|
||||||
|
|
||||||
// -1 is to not push \0 onto stack
|
// -1 is to not push \0 onto stack
|
||||||
for (size_t i = 0; i < expression.len - 1; i++) {
|
for (size_t i = 0; i < expression.len; i++) {
|
||||||
if (validate_char(expression.string[i]).type != Operator) continue;
|
// we track if we reached \0 so we can make sure the last number gets pushed onto the stack
|
||||||
|
// as we only do stack pushing when we reach an operator or in this case \0
|
||||||
|
const bool reached_eof = expression.string[i] == 0x0 && lastOp != i - 1;
|
||||||
|
if (validate_char(expression.string[i]).type != Operator && !reached_eof) continue;
|
||||||
|
|
||||||
const string slice = get_slice(lastOp, i, expression.string);
|
const string slice = get_slice(lastOp, i, expression.string);
|
||||||
if (slice.string != nullptr) {
|
if (slice.string != nullptr) {
|
||||||
@@ -236,6 +252,7 @@ stack split_into_tokens(const string expression) {
|
|||||||
if (ret == 1) throw_error("Failed to push slice into stack");
|
if (ret == 1) throw_error("Failed to push slice into stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reached_eof) break; // do not push \0 into token stack
|
||||||
const int ret = stack_push(&tokenisedExpression, (stackInput){.ch = expression.string[i]}, true);
|
const int ret = stack_push(&tokenisedExpression, (stackInput){.ch = expression.string[i]}, true);
|
||||||
if (ret == 1) throw_error("Failed to push current operator into stack");
|
if (ret == 1) throw_error("Failed to push current operator into stack");
|
||||||
|
|
||||||
@@ -272,13 +289,15 @@ bool validate_expression(const char *expression) {
|
|||||||
|
|
||||||
charResult validate_char(const char c) {
|
charResult validate_char(const char c) {
|
||||||
if (isdigit(c)) return (charResult){Digit, 0};
|
if (isdigit(c)) return (charResult){Digit, 0};
|
||||||
|
// todo maybe we it'd be better to just have it as const instead of creating it every time we call this function
|
||||||
const char *OPERATORS[] = {"+-", "*/", "^", "()"};
|
const char *OPERATORS[] = {"+-", "*/", "^", "()"};
|
||||||
|
constexpr int OP_LEN = sizeof(OPERATORS) / sizeof(OPERATORS[0]);
|
||||||
|
|
||||||
for (int i = 0; i < sizeof(OPERATORS); i++) {
|
for (int i = 0; i < OP_LEN; i++) {
|
||||||
for (int j = 0; j < sizeof(OPERATORS[i]); j++) {
|
for (int j = 0; j < strlen(OPERATORS[i]); j++) {
|
||||||
if (c == OPERATORS[i][j])
|
if (c == OPERATORS[i][j])
|
||||||
// if the operators are parenthesis return 'invalid' precedence
|
// if the operators are parenthesis return 'invalid' precedence
|
||||||
return (charResult){Operator, i == sizeof(OPERATORS) - 1 ? -1 : i};
|
return (charResult){Operator, i == OP_LEN - 1 ? -1 : i};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
stack.c
9
stack.c
@@ -26,6 +26,7 @@ stack stack_init(const DataType type, const int len) {
|
|||||||
break;
|
break;
|
||||||
case IntArray:
|
case IntArray:
|
||||||
stack.data.narr = malloc(arrLen * sizeof(int));
|
stack.data.narr = malloc(arrLen * sizeof(int));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Failed to recognise the DataType for Stack structure");
|
fprintf(stderr, "Failed to recognise the DataType for Stack structure");
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -84,14 +85,16 @@ int stack_push(stack *stack, const stackInput input, const bool pushCharToString
|
|||||||
}
|
}
|
||||||
|
|
||||||
stackData stack_pop(stack *stack) {
|
stackData stack_pop(stack *stack) {
|
||||||
|
// TODO in the future we have to think of solution to clear stackData after we're done with it
|
||||||
|
// TODO maybe fn stack_pop_free()?
|
||||||
if (stack->i == -1) return (stackData){.ret_code = 1};
|
if (stack->i == -1) return (stackData){.ret_code = 1};
|
||||||
stackInput returnData;
|
stackInput returnData;
|
||||||
|
|
||||||
switch (stack->type) {
|
switch (stack->type) {
|
||||||
case StringArray:
|
case StringArray:
|
||||||
returnData.string = stack->data.sarr[stack->i];
|
const size_t len = strlen(stack->data.sarr[stack->i]);
|
||||||
// todo might have to do malloc?
|
returnData.string = malloc(len * sizeof(char) + 1);
|
||||||
// todo ticking timebomb
|
strcpy(returnData.string, stack->data.sarr[stack->i]);
|
||||||
break;
|
break;
|
||||||
case CharArray:
|
case CharArray:
|
||||||
returnData.ch = stack->data.carr[stack->i];
|
returnData.ch = stack->data.carr[stack->i];
|
||||||
|
|||||||
Reference in New Issue
Block a user