New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[First-Class Calc] Add an initial draft #2983
Conversation
765bac1
to
a5ada91
Compare
proposal/first-class-calc.md
Outdated
### Operations | ||
|
||
A calc follows the default behavior of all SassScript operations, except that it | ||
throws an error if used as an operand of a unary or binary `+` or `-` operation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about the *
operator ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default behavior for *
, /
and %
is to throw an error. +
and -
are special in that they default to converting their operands to strings.
proposal/first-class-calc.md
Outdated
* Let `arguments` be the result of [simplifying](#simplifying-a-calcvalue) each | ||
of `calc`'s arguments. | ||
|
||
* If `calc`'s name is `"calc"`, `arguments` must contain a single argument. If |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must
here looks weird to me. It could be interpreted 2 ways:
- it triggers an error if not satisfied (the usual meaning of
must
in a spec) - it must be valid to satisfy the
if
here.
From the explanation in the PR, I understand that you mean the second case. But that would be better written as a condition in the if
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually means "the parser only allows a calc
with name "calc"
that has one argument". I'll make that more explicit.
|
||
* If `calc`'s name is `min`, `max`, or `clamp` and `arguments` are all numbers | ||
whose units are mutually [compatible], return the result of calling | ||
[`math.min()`], [`math.max()`], or `math.clamp()` (respectively) with those |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no link for math.clamp
here ? Why ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It hasn't been added to the math spec yet.
proposal/first-class-calc.md
Outdated
|
||
* Emit `" "`, then the operator, then `" "`. | ||
|
||
* If the operator is `"*"` or `"/"` and the right value is a `CalcOperation` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is not sound when the left or right values are interpolated calc
s. To be safe, parenthesis should be added in such cases (similar to the TODO you have in the simplification algorithm)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a TODO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall, though I wonder if it makes sense to refer to the data type by a different name than just calc
to avoid confusion between ones named "calc" and ones with other names (even just calling the type by the full word "calculation" could make things a bit clearer).
I tend to agree here |
> numbers of any units, and numbers across different known types (length + | ||
> time) are also invalid. | ||
|
||
> TODO: Should we try to simplify `calc(1px + 1% + 1px)`? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For here and other similar TODOs below: what are the cons of simplifying this as much as possible? It seems to me that simplifying to calc(2px + 1%)
would be the most logically consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would require a more complex structure for the AST, because the 1px + 1% + 1px
would be represented as CalcOperation(1px, '+', CalcOperation(1%, '+', 1px))
currently while simplifying such case would require recombining the sum to try another variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternately, it would require complexity in the simplification logic to whether CalcOperation
s can be "flattened" usefully. The CSS spec actually does cover this sort of simplification logic, so there's prior art, but it's definitely more complex.
proposal/first-class-calc.md
Outdated
return-on-investment would also be inherently limited, since we're planning | ||
on gradually transitioning users away from interpolation in `calc()` anyway. | ||
|
||
2. Treating interpolation as a `CalcValue` that participates in the normal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are formally defined below, but it could be helpful to introduce them earlier on and use the terms throughout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't think of a good way to summarize them here, so I added a link to their formal definitions. Let me know if you have a better idea of how to describe them.
proposal/first-class-calc.md
Outdated
**CalcExpression** ::= `calc(`¹ CalcArgument ')' | ||
**ClampExpression** ::= `clamp(`¹ CalcArgument ',' CalcArgument ',' CalcArgument ')' | ||
**CalcArgument**² ::= InterpolatedDeclarationValue | CalcSum | ||
**CalcSum** ::= CalcProduct (('+' | '-')³ CalcProduct)? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't the right hand side actually be CalcSum
to support 1px + 1% + 1px
? the current syntax would require braces to achieve that as 1px + (1% + 1px)
. (making the CalcProduct
a single CalcValue
containing a CalcArgument
enclosed in braces).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right that this is wrong, but I actually intended to make the second clause repeated rather than using recursion to repeat them (that's the structure that's assumed in the Semantics section below).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then the simplification algorithm needs to be updated, as it was describing only operations with 2 terms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, CalculationOperation
only has left
and right
so the necessary change is explaining how this repeated clause gets turned into the struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it works as written, the CalcSum
and CalcProduct
semantics sections assemble the lists into a sequence of CalcOperations
with two operands each.
> numbers of any units, and numbers across different known types (length + | ||
> time) are also invalid. | ||
|
||
> TODO: Should we try to simplify `calc(1px + 1% + 1px)`? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would require a more complex structure for the AST, because the 1px + 1% + 1px
would be represented as CalcOperation(1px, '+', CalcOperation(1%, '+', 1px))
currently while simplifying such case would require recombining the sum to try another variant.
Co-authored-by: Jennifer Thakar <jathak@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
See #818 Co-authored-by: Jennifer Thakar <jathak@google.com>
See #818
Note that this is not expected to be the finalized proposal, and it
has a number of outstanding TODOs. However, since it's a highly
impactful proposal, I want to get a draft committed so we can start
sending it around to stakeholders, getting feedback, and iterating.