I apologize in advance for the vague title. I didn't want to make it overly verbose, so allow me to explain more in-depth below:
I've currently been developing a strong, statically typed language that compiles down to C++(11) code. Using the answers from How does garbage collection work in languages which are natively compiled? as a guide, I've developed a simple runtime system (written in C++ obviously) for my language that essentially consist of a garbage collector.
The way I designed the GC system is that I first created a base object that all other objects representing specific data types in my language (which currently only include int
egers and bool
eans) would inherit from. The GC itself uses a basic references counting algorithm, accessing the field that holds the number of references each object has provide by the base class mentioned above.
The development of the runtime system has come along fine. However, I've just realized somewhat of a problem. Because I have to wrap all data types from my language in their respective objects, this makes the C++ code I generate extremely verbose and clunky for anything bigger than simple expressions. For example, say I had the expression 1 + 2 * (4 - 5) - 6 / (7 + 8)
. This would roughly be transpiled to the following C++ code by my compiler:
*new Integer(1) + *new Integer(2) * (*new Integer(4) - *new Integer(5)) - *new Integer(6) / (*new Integer(7) - *new Integer(8));
As can clearly be seen, the C++ code that was generated is extremely verbose compared to the original expression written in my language. You could imagine how much worse this would look for even more complex expressions.
My question is: How should a problem like this be dealt with? Is this simply a problem created by my inexperience in creating a runtime system, or is this something that normally occurs when compiling? Obviously this isn't a "problem" in the sense that it's inhibiting me from continuing to develop my compiler, but since I do want my compiler to generate read-able C++ code, this should be something that is solved.
One solution I've thought to this problem is to use a method like three address code to break large expressions into manageable parts, but before I implement it I'd like to understand whether this problem has a better solution.
t0:= *new Integer(0) + *new Integer(1);
. Three address code gives the nameless temporary object a name, removes the need for parentheses, and makes your order of operations explicit.* new Integer(n)
might be collapsed. Like in C if you do&a[i]
, which logically says& *(a+i)
(as per definition of[]
). Now you can see that here the&
and*
cancel out, with the result being justa+i
. (Of course, in this example, C has to do this otherwise it'd dereference the array when it shouldn't.)