Repository navigation
[css-values] nested calc() is not defined by its grammar #6506
Description
Hello,
The grammar of <calc-value> (Mathematical expressions - Syntax, CSS Values) does not seem to allow calc() to be used recursively and in other math functions. This is only possible with simple blocks.
<calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
<calc-product> = <calc-value> [ [ '*' | '/' ] <calc-value> ]*
<calc-value> = <number> | <dimension> | <percentage> | <calc-constant> | ( <calc-sum> )
<calc()> = calc( <calc-sum> )
<min()> = min( <calc-sum># )
... other math functions
Would the following change be appropriate?
- <calc-value> = <number> | <dimension> | <percentage> | <calc-constant> | ( <calc-sum> )
+ <calc-value> = <number> | <dimension> | <percentage> | <calc-constant> | ( <calc-sum> ) | <calc()>EDIT 1: or maybe the following change, to also allow other math functions?
- <calc-value> = <number> | <dimension> | <percentage> | <calc-constant> | ( <calc-sum> )
+ <calc-value> = <number> | <dimension> | <percentage> | <calc-constant> | ( <calc-sum> ) | <math-function>
+ <math-function> = <calc()> | <min()> | <max()> | <clamp()> | ...EDIT 2: and now I realize that the following part answer this (part of my) issue.
A math function represents a numeric value [...] and can be used wherever such a value would be valid.
EDIT 3: duplicate of #1781.
To be honest, I'm creating this issue as a pretext to ask for clarifications about other math functions related parts of the specification that I'm not sure to understand correctly. I hope I will not waste anyone's time by asking stupid questions, and that they will help to improve some parts instead.
In the introduction of The internal representation (of math functions), it is written:
The internal representation [of any math function other than
calc()] is an operator node with the same name as the function, whose children are the result of parsing a calculation from each of the function’s arguments, in the order they appear.
The round() function accepts a component value that is not a calculation as its arguments: <round()> = round( <rounding-strategy>?, <calc-sum>, <calc-sum> ). Am I right to think that the above definition was written after adding round() to the specification? round() does not seem to be handled also in the procedure to serialize a math function, which starts with "If the root of the calculation tree fn represents is a numeric value [...]".
For step 3 of the procedure to parse a calculation, it is written:
For every consecutive run of value items in values separated by "*" or "/" operators: [...]
What is a "run" in "every consecutive run of values"? Is it different from "every consecutive values"? Should 1 * 2 * 3 * 4 be reduced to a Product [[1, 2], [3, 4]], [[[[1, 2], 3], 4], or [1, 2, 3, 4]? Does it really matter? I believe that the procedure to simplify a calculation tree will be able to process and return the same result for these 3 different structures anyway, but I'm just not sure why this "consecutive run" term is used.
For step 4 of this same procedure, it is written:
If
valueshas only one item, and it is a Product node or a parenthesized simple block [...]
Given the grammar of <calc-sum>, theoretically a sum can only receive <calc-product>. Is it correct that this means that a Product data structure is different from a component value matching the grammar of <calc-product>? If so, that's quite confusing to me. Is it about to avoid writing the grammar of <calc-sum> with <calc-value> | <calc-product> [ ['*' | '/'] <calc-product> ]?
In step 4 of the procedure to simplify a calculation tree, it is written:
If root is an operator node that’s not one of the calc-operator nodes, and all of its children are numeric values with enough information to compute the operation root represents [...]
Is it correct that root can only be a math function? If so, what is the reason for not using the math function term instead of an operator node that’s not one of the calc-operator nodes?
Also, it seems that none of the remaining steps of this procedure handle a math function containing non numeric values. Does a step 10. Return root is missing?
I see this note above the procedure to serialize a mathematical function:
This section is still under discussion.
But access to the link target is restricted to W3C members. Is it possible to give me clues for the following terms that appear in the different steps of this procedure:
- the root of the calculation tree
fnrepresents fnrepresents- the calculation tree’s
rootnode is
Do they mean different things?
Activity
[-][css-values] nested `calc ()` is not defined by its grammar[/-][+][css-values] nested `calc()` is not defined by its grammar[/+]tabatkins commentedon Aug 11, 2021
The
<number> | <dimension> | <percentage>part of the grammar covers that - those aren't the token productions, but the more generic value productions defined in V&U. In particular,<dimension>covers<length>,<time>, etc if you follow the link. Any function that resolves to a numeric value works there, such asattr()if it's specified to parse to a numeric type.Ah, yeah, that text predates
round()appearing with its non-numeric argument. I'll fix.I'm just using it as it standard English meaning, implying you take the maximal amount. In
1 * 2 * 3 * 4, it's all four values.In normal CSS it does not matter because it can't really be observed, but in Typed OM it's important because they'll produce different object graphs.
No, it's just to properly handle the immediately preceding step. Starting with
1 * 2 * 3, this is turned into the list[1, *, 2, *, 3]. Step 3 collects those into[Product(1,2,3)]. Step 4 then sees that there's only a single child, and lifts it up so thatvaluesis just theProduct(1,2,3)node, rather than wrapping it in a uselessSum(Product(1,2,3)).Contrast with
1 * 2 + 3, where it starts as[1, *, 2, +, 3], step 3 collects the first few into[Product(1,2), +, 3], then Step 4 just dumps all of those items into aSum(Product(1,2), 3)which is meaningful.Yes, that's correct. The following steps handle the calc-operator nodes, so it seemed clearer and less prone to later editing mistakes to explicitly exclude them from this step, rather than use an inclusive term that happens to not include them.
Are you referring to
round()and its keyword argument? If so, then as I said above, that's just a missed edit that I'll fix.If you're referring to anything else, can you provide an example? Non-numeric values in any other case should cause a parsing failure and thus never reach this algo.
Ugh, yeah it is; someone made a mistake and sent technical feedback to the private list in reply to a telcon agenda announcement. Don't worry, it's not relevant anymore and I should just remove it. (The behavior under discussion was resolved years ago and is widely implemented.)
No, they all mean the same thing. I'm just being a little loose with terminology there.
cdoublev commentedon Aug 11, 2021
Thanks a lot. I think everything is clear to me now. I leave it to you to close this issue and/or change its title so that it targets the parts that must considerate the non-numeric argument of
round ().chris-morgan commentedon Aug 25, 2021
The number case is expressed well:
This shows that it’s talking about values rather than tokens, obviously leaving it open for non-literals.
But, to potentially expand the scope of this issue a bit with a closely-related issue, some others don’t make this clear; strings, for example:
This would seem to declare
<string>to only be string literals. But I honestly don’t know if it allows non-literals or not, because some places that take<string>seem to allow non-literal strings implicitly, and others don’t. For example, as well as mentioning<string>,contentspells out<counter>and<target>, which correspond to functions that all produce strings, but doesn’t make any mention ofattr(), which can also produce a string and which works. Butcounters()says it takes a<string>as its second argument, but Firefox at least refuses to acceptcounters(name, attr(join, ".")), which I gather means it’s only accepting a literal string there.This suggests to me that something needs to be changed in spec or implementation on
contentorcounters(), because they seem to be inconsistent.It’d be good for these sorts of types to spell it out like number does. e.g.
Or if it does exclude non-literal strings:
cdoublev commentedon Aug 25, 2021
Recently at the end of my days, the difference between what a type of value (literally) is vs. what a type of value represents, often becomes blurry to me. 🤪
I currently believe that a
<number>is a component value that represent a number. A component value is either one of the preverved tokens (which include<number-token>), a function, or a block.My struggle was more related to missing the quoted definition below, which means to me that a CSS type representing a numeric (CSS component) value, ie. a
<number-token>, a<dimension-token>, or a<percentage-token>, can be replaced (alternatively parsed with the grammar of) a math function, and I learned last week that a math function (a component value that is a function) that replaces a<number>, is itself a<number>:I'm not very used to reading technical specs and english is not my native language, so please take it as my very humble opinion: isn't
attr()defined as a type that can replace a property value when a<string>is expected, ie. it can not replace a function argument that expects a<string>?tabatkins commentedon Sep 8, 2021
That's not intended, it's just not using the parallel better text from the number section.
<string>is a generic production that can be literals or other functional values that return strings.Nope, it's just a value that represents a
<string>, so it can be used anywhere a<string>can be used, such as in a function argument.[css-values-4] Math functions can have non-calculation children. #6506
[css-values-4][editorial] Don't imply that <string> is only literals. #…
tabatkins commentedon Oct 15, 2021
All right, addressed the two lingering issues brought up here:
round()to be handled correctly.<string>section no longer implies that it's only for literal strings; instead, it uses similar wording to<number>and just describes "When written literally...".[css-values-4] Remove outdated link to calc-serialization issue
[css-values-4] A small fix and some tweaks to calc serialization (#11782