Creating an Interpreter for Monty Bytecodes Using C Programming Language

Robert Amoah
6 min readJun 17, 2023

--

PART SEVEN

In case you missed the previous part (PART SIX), you can check it out here: https://medium.com/@mr_robertamoah/creating-an-interpreter-for-monty-bytecodes-using-c-programming-language-67c374b1c8f0

So far, we have seen how the foundation of the program was put together as well as how implementation of the several opcodes were built on top of the foundation. We have done all of these using the stack data structure where the first stack_s node that we add to the linked list becomes the last to print (or remove). It is not the same with queues. With queues, we will have to print the first node first. Let us start by examining an example on the ALX project page.

julien@ubuntu:~/monty$ cat bytecodes/47.m
queue
push 1
push 2
push 3
pall
stack
push 4
push 5
push 6
pall
add
pall
queue
push 11111
add
pall
julien@ubuntu:~/monty$ ./monty bytecodes/47.m
1
2
3
6
5
4
1
2
3
11
4
1
2
3
15
1
2
3
11111
julien@ubuntu:~/monty$

Looking at this example, a queue instruction lets the interpreter know that it is supposed to use a queue data structure instead of a stack data structure. Examining the instructions from queue to the first pall, and the output, we can see that printing starts from 1 to 3 instead of the usual 3 to 1. What this means is that instead of inserting new nodes to the beginning of the linked list as we did earlier, we are to insert new nodes of stack_s structs to the end of the linked list. That is all we will have to do to implement the queue. With the coding, that will require me making some changes to the arg_s struct in the header file, initialize_arguments function, write some code in the queue and stack functions, as well as make changes to the push function. The following are the code changes:

updated arg_s struct
monty.h
initialize_arguments.c
using stack data structure
stack.c
use queue data structure
queue.c
updated to cater for the queue implementation
push.c

What I have done is to introduce a new member to the arg_s struct. This is stack and its an integer. This will be set to 1 on initialization using the initialize_arguments function. This stack member of the arguments global variable, will be set to 1 in the stack function and 0 in the queue function. What this means is that by default, we will use the stack data structure but this can be changed any time during the running of the program with the queue and stack opcodes.

I finally made changes to the push function to cater for the case of the queue. In the push, after the stack has been created, I check if the head is NULL, meaning if the linked list is empty. What I do is just to assign the created stack to the head because it is the only node. When the head is not NULL, I then check whether we are to use the stack data structure. If we are, then I insert the new stack to the beginning of the linked list by assigning the current head to the next of the new stack node and setting the prev member of the current head to the new stack node. Once this is done, I then set the head to the new stack node. We have seen this earlier. The main change to this code is the case where we are not using stack. With the help of a temporary variable (tmp), I loop through the linked list starting from the head till last node, which has its next member to be NULL. Once I have the last node in tmp, I set its next member variable to the new stack node and set the prev member of the new stack node to tmp. This is all that is needed to implement the queue opcode.

Simply compile your code with the given gcc command and run the program with the bytecodes we examined earlier. Below is an image that shows the content of the Monty file and the output of the program.

standard output

The queue command pushes 1 to the end. Stack 2 is pushed to end and so is stack 3. The pall function then prints from the beginning hence prints 1 to 3. The stack function sets the stack member of arguments to 1. Stack 4 is now pushed to the beginning of the linked list, and so are stacks 5 and 6. We now have a stack looking like 6, 5, 4, 1, 2, and 3. The add function will then add 6 to 5, and assigned the value to stack 5 while removing stack 6. We will now have 11, 4, 1, 2, 3. These are printed by pall. The next queue function sets the stack member of our arguments to 0. Therefore, the next push instruction adds stack 1111 to the end of the linked list giving us 11, 4, 1, 2, 3, 1111. The next add function will also add 11 to 4 and set stack 4 to the result while removing stack 11. Our final linked list will therefore be 15, 1, 2, 3, 1111.

We are done implementing all the opcode functions associated with Monty interpreter. I have a few things to say, though, in order for you to get all scores in the project. Make sure you use the betty linter to check all your C files and header files. Also, check if there are any memory leaks or memory access errors by using valgrind command. One important thing to note when using valgrind to track memory issues is the usage of “-g” flag with the gcc compilation commands. For example: “gcc -g -Wall -Werror -Wextra -pedantic -std=c89 *.c -o monty”. What this does is it allows valgrind to give you the file names and line numbers where the memory issues occur. It is really helpful. You can check it out.

Lastly, I ran the program in valgrind mode so you see that I actually deal with freeing up memory allocations.

standard output

All the files for this program can be found on GitHub. Link to the files: mr-robertamoah/new_monty (github.com)

Drawing the curtains, tell me one thing you learnt from these series I have written, as well as one thing you disliked. Thank you for reading. Thank you for letting me know what your thoughts are.

👋👋.

--

--

Robert Amoah

Full-stack web developer #laravel #nestjs #vue #react Mobile Application Development #reactnative DevOps and Cloud enthusiast #linux #bash Love Data Science