Re: Cascade: Supplement instead of Overwrite

On Sun, 10 Oct 1999, Sjoerd Visscher wrote:

>> But you still have to modify the syntax of each property to say
>> that the function can be accepted. For example, just inventing the
>> attr() function didn't mean that any property could take it -- it
>> has to be explicitly stated in the syntax of the 'content'
>> property.
>>
>> Maybe this should be changed, but I imagine that would be quite a
>> complex task. One of the CSS2 drafts touched on this. One problem
>> is that you have to define the return type of the functions [...]
>
> I don't know about that. All property values are plain text, which
> represent the different types.

Eek, no. Most property values are keywords. Keywords are atomic and
cannot be broken down. "plain text" can be broken down. In CSS, plain
text is always quoted.


> There is no type-casting, and there is no property that needs to
> know what type it's value has.

Interesting assertion.

   li:before { content: "this is plain text"
                        url(this_returns_a_graphic); }

I would suggest that 'content' would rather like to know what type its
values are. If it interpreted the return value of the url() function
as plain text, the results would be somewhat disappointing (and ugly).

Another example: "900" is a keyword in the context of <font-weight>
but a number in the context of <line-height>. This is important for
parsing things like the 'font' and 'border' properties:

   x { border: solid rgb(1,2,3) thick; }
                |     |          +---- this returns a <length>
                |     +---- this returns a <color>
                +---- this has a type of <border-style>

It is only by knowing the types of the three values that you know
which is the style, which is the color and which is the width of the
border!


> If the implementation simply replaces the function with the returned
> text of the function, just like an author would put it there, it's
> impossible to get problems.

This is the equivalent of document.writeln in embedded JavaScript --
and this requires the parser to be reentrant. Ouch.

(You can't on the one hand be saying that the value list is parsed on
the first pass and on the second hand be saying that functions return
unparsed data -- this is inconsistent!)

 
>> CSS strategically avoids that by only allowing functions in
>> predescribed places which know how to cope with each of the
>> functions. Once a return type for each function is defined, then
>> you have to explicitly say which types each property understands.
>> This is all not as obvious as it may seem -- The attr() function,
>> for example, can return a string, a URI, a number, a space
>> delimited keyword list, etc. But how do you know that it has
>> returned a URI and not a string?
> You cannot. But in the rare case this is a problem, you could use
> url(attr())

No, you couldn't, at least not without some quite big changes to the
current grammar:

   declaration
     : property ':' S* expr prio?
     | /* empty */
     ;
   expr
     : term [ operator term ]*
     ;
   term
     : unary_operator?
       [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
         TIME S* | FREQ S* | function ]
     | STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
     ;
   function
     : FUNCTION S* expr ')' S*
     ;

...where:

   "url("{w}{string}{w}")" {return URI;}
   "url("{w}{url}{w}")"    {return URI;}
   {ident}"("              {return FUNCTION;}

So url(attr()) would not be recognised as a function in a function, as
the first nested "(" would (if I read this correctly...) be a syntax
error. (Thankfully, this means that it would be possible to extend the
syntax without risk of breaking CSS1 pages.)


Also, you couldn't do this:

   x { border: attr(style) attr(color) attr(width); }

...because if attr() returns text, then all three would be invalid,
and if attr() was somehow context-sensitive (good luck specifying
that!), then you could not distinguish between strings and keywords in
cases where both are acceptable (for example 'content'):

   z:before { content: attr(text) attr(symbol); }

...with

   <z text="hello" symbol="open-quote" />

(Note that according to the current spec, attr() always returns a text
string, so the above is not ambiguous -- but doesn't result in an
opening quote either.)


> The function 'url()' could be seen as a function that converts an
> URI to an URL, and returns the URL as plain text.

But url() doesn't return plain text, it returns the content of the
document referenced by the URI! Otherwise, 

   li:before { content: "this is plain text"
                        url(this_returns_a_graphic); }

...would result in <li> elements with this before them:

   this is plain texthttp://www.host.tld/this_returns_a_graphic

...which is distinctly not what we are looking for!

 
> Please reread the part about bottom-up and top-down. You
> misunderstood bottom-up as being from the bottom of the table you
> mentioned, where I meant from lowest specificity to the highest.

Hm. I am not knowledgeable enough in algorithms, efficient coding and
related areas to say which is the best -- in any case, since CSS
doesn't specify actual algorithms (rightly leaving this up to the
implementors) this is rather academic.

-- 
Ian Hickson
"I take a Professor Bullett approach to my answers. There's a high
probability that they may be right."
  -- Dr Snow; Mechanics Lecturer at Bath University; 1999-03-04

Received on Sunday, 10 October 1999 19:03:55 UTC