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

Reply via email to