On 11/30/2009 07:59 PM, Chad J wrote:
If you care for a bit of reading, I'll tell you my story and ping an idea.
This is about the property expression rewrite of course. I'd love to
just use the current convention in dmd and write the rewrite as a
non-recursive function that gets called at every point in the tree
whenever someExpr->semantic(sc) is called. However, there's a snag.
Personally, I've come to hate this pattern (just from reading DMD src).
It seems like the antithesis of code reuse.
When doing the property rewrite, the inner-most subexpression will need
to generate the outermost temporaries.
An example:
prop1.prop2 += foo;
AFAIK, the compiler sees this as ((prop1).prop2) += foo; or
((prop1()).prop2()) += foo; after resolveProperties is called. So prop1
is the innermost expression.
It would be rewritten as this comma expression:
(auto t1 = prop1()),
(auto t2 = t1.prop2()),
(t2 += foo),
(t1.prop2(t2)),
(prop1(t1))
This is an interesting problem. I like it. Is this rewrite condoned by
the powers that be? It'd be fun to implement if I ever get this far in
my semantic analyzer.
So t1 is the outermost temporary, and it corresponds to prop1, the
innermost expression.
This is problematic in dmd's traditional approach, because the semantic
pass on prop1 does not have access to it's enclosing expression (as far
as I can tell). If it did, I'd just go to the root of the tree, uproot
the tree, stick it in a comma expression, and just start nesting:
I'm not convinced you need access to the enclosing expression to make
this rewrite happen. Working it out on paper, it appears to be a matter
of a few AST copies, keeping track of your t's, and building your comma
tree (in two directions simultaneously) as you traverse the AST via eg
semantic.
Here's some chicken scratch that more or less illustrates what I
envision going on (I can't attest that DMD's ASTs look exactly like
this, but I assume they're similar):
http://personal.utulsa.edu/~ellery-newcomer/scribble.png
How much effort it would entail is another matter though. I would assume
you could just add a field or two to struct Expression for pushing
information up the tree. It seems like copying functionality is already
there. Pushing 't' information across the tree would probably propagate
to each special case and be the most annoying part. But basically, every
time you come across a property, you generate a new symbol and pair of
pre/post trees. Then at the top, you make a single tree copy with the
last symbol generated.
Now for the idea.
Currently I'm planning an approach that's a bit more minimalistic than
the visitor pattern; just to solve my immediate problem. I will define
these methods in Expression:
virtual Expression *getChild(unsigned index);
virtual void setChild(unsigned index, Expression* val);
virtual unsigned numChildren();
I can imagine this would be welcome regardless of how you set about your
rewrite procedure. I came across a need for something like this as I was
building my semantic analyzer a while back. Can't wait for school to get
out so I can get back to work.
Then I will implement them as needed in the subclasses of Expression. I
figure I'd have to write this logic into my function anyways, so I might
as well make this usable to others. I've already written a draft of the
property rewrite that uses these, so I know they get the job done in
principle. I just hope this will all be OK in the patch (or even holds
up under debugging).
So yeah, if I haven't put you to sleep already, please let me know if I
messed up somewhere.
I do have one nitpick:
auto t1 = p1();
isn't an expression, it's a declaration. That could potentially make
things difficult for you, particularly when your property assignment is
deep within an expression tree.
- Chad