patternpythonMinor
Improving readability of Boolean adder generator?
Viewed 0 times
booleanreadabilitygeneratorimprovingadder
Problem
Today I've noticed that the source code for a Boolean adder is very repetitive, so I wrote a Python script to generate the C source code. The script generates an adder of varying size depending on the length of the added numbers, but the script is very hard to read and understand. How can I improve the readability of my script so that others can understand its operation more easily?
```
number1 = "98530"
number2 = "84309"
number1 = '{0:b}'.format(int(number1))
number2 = '{0:b}'.format(int(number2))
count = len(number1)
number1 = str(number1)
number2 = str(number2)
print """#include
typedef char pin_t;
#define IN const pin_t *
#define OUT pin_t *
#define PIN(X) pin_t _##X; pin_t *X = & _##X;
#define V(X) (*(X))
#define NOT(X) (~(X)&1)
#define XOR(X,Y) ((NOT(X)&(Y)) | ((X)&NOT(Y)))
void halfadder(IN a, IN b, OUT s, OUT c)
{
V(s) = XOR(V(a), V(b));
V(c) = V(a) & V(b);
}
void fulladder(IN a, IN b, IN ic, OUT s, OUT oc)
{
PIN(ps); PIN(pc); PIN(tc);
halfadder(/INPUT/a, b, /OUTPUT/ps, pc);
halfadder(/INPUT/ps, ic, /OUTPUT/s, tc);
V(oc) = V(tc) | V(pc);
}
"""
print "void fourbitsadder(",
for n in range(0,count):
print "IN "+"a"+str(n)+", ",
print "\n\t\t\t\t\t",
for n in range(0,count):
print "IN "+"b"+str(n)+", ",
print "\n\t\t\t\t\t",
for n in range(0,count):
print "OUT "+"o"+str(n)+", ",
print "\n\t\t\t\t\t",
print "OUT overflow)"
print """
{
\tPIN(zero); V(zero) = 0;
"""
print "\t",
for num in range(0,count):
print "PIN(tc"+str(num)+");",
print "\n\tfulladder(/INPUT/a0, b0, zero, /OUTPUT/o0, tc0);"
for num in range(1,count-1):
print "\t"+"fulladder(/INPUT/"+"a"+str(num)+", "+"b"+str(num)+", "+"tc"+str(num-1)+", "+"/OUTPUT/"+"o"+str(num)+", "+"tc"+str(num)+");"
print "\tfulladder(/INPUT/a"+str(count-1)+", "+"b"+str(count-1)+", "+"tc"+str(count-2)+", /OUTPUT/o"+str(count-1)+", "+ "overflow);"
print "}"
print """
int main()
{
"""
print "\t",
for num in range(0,count):
print "PIN(a"+str(num)+");",
pr
```
number1 = "98530"
number2 = "84309"
number1 = '{0:b}'.format(int(number1))
number2 = '{0:b}'.format(int(number2))
count = len(number1)
number1 = str(number1)
number2 = str(number2)
print """#include
typedef char pin_t;
#define IN const pin_t *
#define OUT pin_t *
#define PIN(X) pin_t _##X; pin_t *X = & _##X;
#define V(X) (*(X))
#define NOT(X) (~(X)&1)
#define XOR(X,Y) ((NOT(X)&(Y)) | ((X)&NOT(Y)))
void halfadder(IN a, IN b, OUT s, OUT c)
{
V(s) = XOR(V(a), V(b));
V(c) = V(a) & V(b);
}
void fulladder(IN a, IN b, IN ic, OUT s, OUT oc)
{
PIN(ps); PIN(pc); PIN(tc);
halfadder(/INPUT/a, b, /OUTPUT/ps, pc);
halfadder(/INPUT/ps, ic, /OUTPUT/s, tc);
V(oc) = V(tc) | V(pc);
}
"""
print "void fourbitsadder(",
for n in range(0,count):
print "IN "+"a"+str(n)+", ",
print "\n\t\t\t\t\t",
for n in range(0,count):
print "IN "+"b"+str(n)+", ",
print "\n\t\t\t\t\t",
for n in range(0,count):
print "OUT "+"o"+str(n)+", ",
print "\n\t\t\t\t\t",
print "OUT overflow)"
print """
{
\tPIN(zero); V(zero) = 0;
"""
print "\t",
for num in range(0,count):
print "PIN(tc"+str(num)+");",
print "\n\tfulladder(/INPUT/a0, b0, zero, /OUTPUT/o0, tc0);"
for num in range(1,count-1):
print "\t"+"fulladder(/INPUT/"+"a"+str(num)+", "+"b"+str(num)+", "+"tc"+str(num-1)+", "+"/OUTPUT/"+"o"+str(num)+", "+"tc"+str(num)+");"
print "\tfulladder(/INPUT/a"+str(count-1)+", "+"b"+str(count-1)+", "+"tc"+str(count-2)+", /OUTPUT/o"+str(count-1)+", "+ "overflow);"
print "}"
print """
int main()
{
"""
print "\t",
for num in range(0,count):
print "PIN(a"+str(num)+");",
pr
Solution
The C code may not look pretty in this particular case because it's designed to run fast, but the Python code should look beautiful because it's designed to be clearly readable to someone unfamiliar with it. There are a number of things you can do to achieve this.
Smaller functions
Break down your code into functions with descriptive names, so the whole process can be seen in a few lines, rather than a few pages. This also makes it easier to spot other ways to improve your code. Ideally each function should only do one thing. If you have to scroll to read a function, ask yourself whether it is too long. A short function can be grasped even by someone who is seeing your code for the first time:
Multilines
Indent multiline expressions to make it clear they are not unrelated separate lines.
rather than
If you want to make more explicit which whitespace is part of the Python code and which is part of the C code, you can use parentheses to continue over several lines:
In this case it may be just as readable to simply use one line:
Any of these is preferable to multiline expressions without indentation.
Ease of reuse
If other people want to use your code in a program of their own, they can only use it to print the C program. If your code was in a function that returned a string, you could still call that function to print the string but other people would also be able to call the function to save the string to file or assign the string to a variable (or even pass it directly to a C compiler). Avoiding using print until the end will make the code much easier to reuse (for yourself and others).
User input
It appears that you have fixed the two numbers to be added, even though your Python code is already sufficiently general to take an arbitrary two numbers as arguments. Since you've already written the code to handle arbitrary numbers, go ahead and give the user the chance to provide their own numbers. If you have a function that returns a string containing the full C program, say
You might like to think about allowing the C code to take two numbers as arguments too, but this is just a review of the Python code...
Is this a compiler?
In answer to your final question, no I wouldn't describe this as a compiler. It doesn't take source code as input. If it took an arbitrary Python program as input, and output a corresponding C program, then it could be described as a compiler. I would describe your code as automatic programming rather than a compiler.
Correctness
I'm mainly reviewing for readability and usability but this question occurred to me:
I notice that you only test the length of the binary string for number1. Does your code still work as intended if number1 < number2?
Smaller functions
Break down your code into functions with descriptive names, so the whole process can be seen in a few lines, rather than a few pages. This also makes it easier to spot other ways to improve your code. Ideally each function should only do one thing. If you have to scroll to read a function, ask yourself whether it is too long. A short function can be grasped even by someone who is seeing your code for the first time:
def c_code(number1, number2):
return macros() + tailored_code(number1, number2)Multilines
Indent multiline expressions to make it clear they are not unrelated separate lines.
print """
{
\tPIN(zero); V(zero) = 0;
"""rather than
print """
{
\tPIN(zero); V(zero) = 0;
"""If you want to make more explicit which whitespace is part of the Python code and which is part of the C code, you can use parentheses to continue over several lines:
print("{\n"
"\tPIN(zero); V(zero) = 0;"
)In this case it may be just as readable to simply use one line:
print "{\n\tPIN(zero); V(zero) = 0;"Any of these is preferable to multiline expressions without indentation.
Ease of reuse
If other people want to use your code in a program of their own, they can only use it to print the C program. If your code was in a function that returned a string, you could still call that function to print the string but other people would also be able to call the function to save the string to file or assign the string to a variable (or even pass it directly to a C compiler). Avoiding using print until the end will make the code much easier to reuse (for yourself and others).
User input
It appears that you have fixed the two numbers to be added, even though your Python code is already sufficiently general to take an arbitrary two numbers as arguments. Since you've already written the code to handle arbitrary numbers, go ahead and give the user the chance to provide their own numbers. If you have a function that returns a string containing the full C program, say
c_code, then you can include something like the following at the very end of your code:# Handle the case where this program is called from the command line.
if __name__ == '__main__':
import sys
arguments = sys.argv
if len(arguments) == 3:
print(c_code(arguments[1], arguments[2])
else:
print(input_requirements())You might like to think about allowing the C code to take two numbers as arguments too, but this is just a review of the Python code...
Is this a compiler?
In answer to your final question, no I wouldn't describe this as a compiler. It doesn't take source code as input. If it took an arbitrary Python program as input, and output a corresponding C program, then it could be described as a compiler. I would describe your code as automatic programming rather than a compiler.
Correctness
I'm mainly reviewing for readability and usability but this question occurred to me:
I notice that you only test the length of the binary string for number1. Does your code still work as intended if number1 < number2?
Code Snippets
def c_code(number1, number2):
return macros() + tailored_code(number1, number2)print """
{
\tPIN(zero); V(zero) = 0;
"""print """
{
\tPIN(zero); V(zero) = 0;
"""print("{\n"
"\tPIN(zero); V(zero) = 0;"
)print "{\n\tPIN(zero); V(zero) = 0;"Context
StackExchange Code Review Q#33038, answer score: 3
Revisions (0)
No revisions yet.