NovaShell - Undefs
Tags: beginners, programming, multivalue, python
Published: 2022-06-12 00:15:44

In the previous post, I went over ifdefs and their benefits. Unfortunately D3 10.2 doesn't handle the define statement. It is able to do ifdefs but without the defines working, it's all pointless. This was fixed in 10.3 so NovaShell only works on UniVerse and D3 10.3 and onwards.

To get NovaShell to work on D3 10.2, I'll need to remove the ifdefs and remove any UniVerse specific code. I could do this manually as doing just a search for ifdefs would be straightforward but every time I make a change, this step would need to happen.

I figured that there must be a tool that does this already. It seems likely that someone would want to do the conditional compilation step without actually compiling the code. There should exist a program that can read in the defines and then strip out the ifdefs and unneeded code blocks.

I was right! There was a tool that does this for C.

http://dotat.at/prog/unifdef/

Unfortunately I tried to get it to work with NovaShell but that proved to be a bit harder than expected. I decided it'd be faster to just write my own routine to do it.

It's a very simple program as it needs to only parse a few words and because I control the input and I don't plan on making this robust, I don't need to worry about any edge cases.

I simply read in a file in python and loop through the lines. If I find any defines, I added the symbol to a symbol table. If I find any ifdefs, I check to see what symbol its using and if its in the symbol table. If it isn't then remove that code block, if it does exist, then keep that code block.

The trickiest part would be the nesting of ifdefs but that is also a simple issue. This is actually a common interview question which was fun to realize. The problem boils down to balancing parenthesis which is done with a stack.

``` python
#!/usr/bin/env python3

import sys

f = open(sys.argv[1], "r")

lines = f.readlines()

symbolTable = { }

counter = 0

while counter < len(lines):
    line = lines[counter]
    counter = counter + 1

    if "$DEFINE" in line:
        line = line.strip()
        _, symbol = line.split(" ")
        symbolTable[symbol] = True
        continue

    if "$IFDEF" in line:
        line = line.strip()
        _, symbol = line.split(" ")
        if symbol in symbolTable:
            continue
        line = lines[counter]
        stack = 1
        while stack != 0:
            if "$IFDEF" in line:
                stack = stack + 1
            if "$ENDIF" in line:
                stack = stack - 1
            counter = counter + 1
            line = lines[counter]
        continue

    if "$ENDIF" in line:
        continue

    print(line[:-1])

f.close()
```

This is the entire script. It's pretty dirty but it works and cleans up my program so that I could compile it under D3 10.2.

I saved this as undef and made it executable and then placed it in my bin.

To run it:

```
$ undef NSH > NSH.D3
```

This would give me NSH file with all the ifdefs stripped out and based off the defines, it will keep only the relevant code blocks.

I imagine the C version that I found above works in a similar way and probably handles things much better but writing this out was a fun little detour.