Develop a Calculator in C - Part I

Develop a Calculator in C - Part I

Introduction

The C Programming Language is well known for its steep learning curve.

It easily catches learners - particularly beginners, off-guard and put them on the pedestal.

However, aside the fact that the world moves on the wheels of C, what more can one be proud of than to demonstrate a working project fashioned with this language, in the hopes others in the same shoes may be inspired to keep up with their efforts.

So, this is a walk-through tutorial on how I lavished the Christmas holidays in the world of C programming, and developed ASHCulator, a calculator that echoed the festive spirit.

A pint-sized marvel, this creation draws inspiration from Python's mini calculator, driven by an earnest endeavor to sharpen the foundations of the C language.

For those eager to explore further, the entire source code is available on GitHub.

What is ASHCulator?

ASHCulator is a console-based calculator. It was designed to tackle basic arithmetic computations. It harnesses the power of the BODMAS Mathematics principle and resolves problems spanning addition, subtraction, multiplication, division, and modulo operations. BODMAS stands for Brackets, Of (multiplication), Division, Multiplication, Addition and Subtraction.

It was deliberate to limit the capabilities of the calculator. Creating large programs can easily overwhelm. Therefore, breaking such projects into smaller working code bases has the surprising effect of boosting confidence and reinforce learning. The likes of square roots, powers, exponents among others can therefore wait.

With the air cleared on the calculator's complexity, we can start examining its structure and develop along as we go.

ASHCulator from Top Down

ASHCulator executes in a loop. It presents the ashculate ?> prompt for input each time round the loop. This can be thought of it whispering, What's next? After producing the result of each problem, It quickly throws the question at you again.

This will continue indefinitely until you command it to pause, otherwise, it requests for all your problems to solve. Yes, we know this is impossible but it is just one of the fun things that make programming awesome; the feeling that you simplistic software can solve all your problems.

Defining a Struct

By now, I'm certain we are eager to start bisecting the bare constructs of the calculator. So, let's kick off by defining a struct. Also known as structures, structs are used to group several related variables at one place. Unlike arrays, the members of a struct can be of any data type.

/* struct definition */
struct var_data
{
    char *args;
    size_t len;
};
typedef struct var_data INFO;

/* initializer for struct */
#define INFO_INIT {NULL, 0}

We begin with only two members and add more as the need arises. The pointer variable args will briefly hold the user's input or the problem statement, and the len will capture its length. The INFO_INIT definition initializes the struct, and set the stage for variable interplay.

Structs are mostly defined in header files, and this gives them global scope. Therefore, all functions and variables defined in a source file that includes the header file can access and update the members of the struct. Also, you can create variables of the struct type, pass struct instances between functions, and use the struct in various ways.

So by this, we imply that the two struct members will be made accessible to several functions and variables across several source files that will include the header file.

Reading Input from the Keyboard

Before ASHCulator can actually start crunching some computations, it must be fed with some problem that lie within its ability to solve.

To find out whether it could solve a particular problem, it first reads it from the standard input, simply known as stdin in C terminology.

There are several ways you can read inputs in C. Even when using resources from the standard library, you can still tweak things to suit your preferences.

Despite this much freedom, we will implement a very simplistic approach with the help of the getline() function as shown in the following snippet.

#include <string.h>
#include <stdlib.h>

/* readLine - read user input */
ssize_t readLine(INFO *info)
{
    ssize_t len;

    len = getline(&info->args, &info->len, stdin);
    if (len == -1)
    {
        if (feof(stdin))
            exit(EXIT_SUCCESS);
        else
        {
            perror("readline");
            exit(EXIT_FAILURE);
        }
    }

    return (len);
}

It is not surprising that, the first use of our struct is with the function to read user input. As discussed earlier, the global scope of the struct makes it possible for its members to access data, store them temporarily and share them with other variables and functions where they are accessible.

So we pass to this function a pointer variable of type INFO - the struct we defined earlier. We then pass the addresses of the args and len variables to the getline() function to read the input from stdin.

According the manual, the getline() function reads an entire line, and stores a reference to the address in a string pointer variable passed as its first argument, which is args in our case. It also stores a reference to the length of the input in len.

On success, it returns the length of the line of input. However, in the event it fails to read input, it returns -1 and this might mean one of two things: either the user did not provide any input or it just failed while reading and we're sure to handle both cases.

The Entry Point for ASHCulator

At this point, we have implemented the readLine() and we have the problem statement. But where do we begin from? We need a central control point from which we initialize variables, regulate the flow of input and terminate the program gracefully.

So, we'll define the main() function to be the starting point for execution of our mini calculator.

#include <stdio.h>

/* main - entry point */
int main(void)
{
    INFO info[] = { INFO_INIT };
    int len;

    do {
        printf("ashculate ?> ");
        len = readLine(info);

        if (strcmp(info->args, "exit\n") == 0)
            break;

        printf("[%d]: %s\n", len, info->args);
    } while (1);

    free(info->args);

    return (0);
}

Now, let's walk through the main function to see what's happening.

We first declare an array of type INFO struct and initialize it with the INIFO_INIT. Variables of type array and pointer are much friendlier when they're initialized upon their declaration before doing anything with them.

We then use the do..while loop with the condition set to 1 to achieve our objective of consistently asking the user, What's next?. This loop is also preferred as it will always execute any code block within its curly braces, before checking whether the condition has been met.

With an infinite loop set up, we hand the power over to the user to determine when to bow out. Thus, we compare the line of input with the "exit\n" instruction with the help of the strcmp() function from the standard library to signal the termination.

The strcmp() function returns an integer value that is either less than, equal to or greater than zero based on a lexicographical comparison of its first argument with the second.

It'll therefore return a value of 0 if the user enters exit, and we initiate the break statement, to exit the loop. We then de-allocate any resources used and terminate the program.

Let's pause a bit here. You can see the newline character added to the exit instruction. This was deliberate as the getline() function automatically appends a newline character at the end of a line when the enter key is pressed.

This is not the best approach but it provides a convenient way to exit the loop. We will handle this situation gracefully in later stage of the development.

The printf() line prints the length of the input in square braces, followed by a colon and the actual line of input.

Complete Code Listing

We've done enough talking and created code snippets to demonstrate how to accomplish a particular task.

Now, let's see how everything comes together in one huge source file.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* struct definition */
struct var_data
{
    char *args;
    size_t len;
};
typedef struct var_data INFO;

/* initializer for struct */
#define INFO_INIT {NULL, 0}

/* function prototypes */
ssize_t readLine(INFO *);

/* main - entry point */
int main(void)
{
    INFO info[] = { INFO_INIT };
    int len;

    do {
        printf("ashculate ?> ");
        len = readLine(info);

        if (strcmp(info->args, "exit\n") == 0)
            break;

        printf("[%d]: %s\n", len, info->args);
    } while (1);

    free(info->args);

    return (0);
}

/* readLine - read user input */
ssize_t readLine(INFO *info)
{
    ssize_t len;

    len = getline(&info->args, &info->len, stdin);
    if (len == -1)
    {
        if (feof(stdin))
            exit(EXIT_SUCCESS);
        else
        {
            perror("readline");
            exit(EXIT_FAILURE);
        }
    }

    return (len);
}

Compiling and Executing

Now, let's compile the code using the following:

$ gcc -Wall -Werror -Wextra -pedantic -std=gnu89 0_ashculate.c -o 0_ashculate

Next, we execute the resulting object as shown:

$ ./0_ashculate

Testing the Calculator

Test your calculator and you should have results similar to what's shown in the image below:

Conclusion

We conclude the first part of ASHCulator. In this part, we created functions to read input from the keyboard and print everything onto the screen.

You can imagine the feeling on that Christmas night when I got to this stage. Taking on daring projects with a language like C can be rewarding. And this happens to be just one of the awesomeness of programming.

Thanks for reading this first part of the tutorial. I really enjoyed writing it as well as the other parts, which I encourage you to explore them.

Do you have any questions or suggestions, let me know your thoughts in the comments.