>
> I'm thinking of cases where, in CSS terms, a DOM node has to be split into multiple boxes. For example in HTML, <span>aa <span>bb cc</span> dd</span>, where a soft line break is required between bb and cc. In the example you sent, a WrapBox doesn't have to split any child boxes.
Yeah, I'm thinking for this case, we can take advantage of a selector-like extension as described at the bottom of the table tutorial.
We want to consider "aa", "bb", "cc", and "dd" all as children of the closest ancestral block formatting context (I think that was the term), even if they happen to be grandchildren or further in the parse tree. So, I'd like to write something like:
class LineWrappingBFC {
children {
words : Node = (./Node[display==Inline || display==InlineBlock])* //tweaked further to reach but not go into floats
}
actions {
loop words:
/* code as in the previous email */
The table tutorial manually did this for a column selecting its cells as a search on the table rows, and I'm pretty sure we can code generate code to do it from a selector-like form shown above.
Further out, I'm curious about handling this code in terms of full automated verification/synthesis, not just code generation. In the table example, I added a few constraints to tell the synthesizer how to pretend it's a normal attribute grammar, but I suspect it's possible to (soundly) fully automate these hints. I should stress that this is not essential for running code nor code generation :)
>
> Another particularly delicate part of CSS layout is handling "overflow:auto", because it involves circular dependencies. The presence of a scrollbar affects the available width or height within the element, which affects whether scrollbars are needed. In Gecko we effectively try all four possible solutions and pick the "optimal" one (let's say, the one showing the fewest scrollbars, preferring to show a vertical scrollbar over a horizontal one), with appropriate short-circuiting and incrementalization optimizations.
>
That sounds like a good one to look at after floats. We have local short-circuiting for expressions, which gets us part of the way there. For example, in "x := a ? f(b) : g(c)", we don't need to compute both "f(b)" and "g(c)". However, globally, it still requires computing "a", "b", and "c". Posing a challenge, it sounds this is a non-local short-circuiting. In particular, the choice in "a" depends on what we get in "x" (a cycle), which in turn gives an iterative computation yielding different "b" and "c" in each iteration until we are happy with "x".
Such an iterative computation seems doable: we might phrase this case as a trigger to an ordered sequence incremental computations which stops on the first satisfactory attempt. For the foreseeable future, this may be in the category of code generation commands / patches that the synthesizer doesn't fully understand, which is fine.
Longer-term, it does make semantic sense to declaratively control non-local short-circuiting. We can view non-local short-circuiting as "pull" dataflow where, currently, we use "push". Currently, if you want a pocket of "pulls" within an overall "push", you'd have to handle that within external functional calls (kind of how the table code computes the grid). Combining them does make sense, sort of like the reverse of Erik Mejier's LINQ framework: there, the default is pull (~SQL), while sometimes you want push (RxLINQ).
When we write up floats, I'll try to shed a bit more light into what all this means.
> I'm loving your work BTW :-).
Thanks :) It's fun and part of an itch I've had for years ("what is CSS??", "I'd like to build tools for CSS but it's complicated...") :)
- Leo