-
-
Notifications
You must be signed in to change notification settings - Fork 255
Modify grammar to support Private Fields proposal: #260
Conversation
src/parser/expression.js
Outdated
@@ -1014,6 +1022,15 @@ pp.parseIdentifier = function (liberal) { | |||
return this.finishNode(node, "Identifier"); | |||
}; | |||
|
|||
pp.parsePrivateName = function (liberal) { | |||
const isPrivate = this.eat(tt.hash); | |||
const node = this.parseIdentifier(liberal); |
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.
Should this be parsePropertyName?
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.
Im following the naming convention for publicProps (fields => props), but I think for this one you are right.
As a side note, we agreed that we will reconcile the naming for a mayor version.
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'm not talking about naming convention, I'm talking about grammar. The grammar for Identifier is more restrictive than IdentifierName/property names, in that it doesn't allow keywords. The grammar for private state specifically allows keywords as the name (just like ordinary properties).
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 believe this is covered by passing liberal
as true
through to parseIdentifier
(although it probably should be hard-coded to true here, rather than an arg to parsePrivateName
.
parseIdentifier
only restricts using keywords when liberal
is falsy
src/parser/statement.js
Outdated
if (!this.hasPlugin("classPrivateProperties")) { | ||
return this.raise(this.state.pos, `Unexpected character '${tt.hash.label}'`); | ||
} | ||
this.next(); |
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.
Will this ignore whitespace? In the current spec, whitespace between the # and the IdentifierName is not allowed (though Waldemar proposed that it be allowed).
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 will test it and probably I should write a test for it.
ast/spec.md
Outdated
# PrivateName | ||
|
||
```js | ||
interface PrivateName <: Expression, Pattern { |
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'm curious to hear other's thoughts, but I'd probably have expected this to be a normal Identifier
, rather than having a specific subtype for the private node.
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 grammar of PrivateNames is pretty different from Identifiers, in terms of where they can be used. It's been suggested that the grammar in the specification be refactored to not call it PrivateName but more like PrivateReference, consisting of the two individual tokens. To me, it makes sense that they are treated differently from identifiers.
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.
Apart from dan's comments about grammar, @loganfsmyth, I though it was cleaner (at least while in stage2) to make additive changes when possible rather than mutating existing nodes, like adding a private
attribute for this case. We can recondile in the next mayor version, once public fields, private fields et al, are in +Stage3
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.
Identifier
It is an identifier with a specific type.
It would be confusing for developers because they will be forced to check the type of the identifier.
Most of them will omnit it and will cause some bugs.
PrivateName
More explicit.
I don't have much context here, but I guess most transformation within a class will be with both PrivateNames and Identifier.
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.
My main motivation is consistency. You already always need to check what an identifier is. It could be a local variable, it could be a class property, it could be an object property, it could be an import, it could be imported binding. Identifier
is essentially a catch-all for non-keyword values in the original source.
My expectation would be that the ClassPrivateProperty
would be the clarifying context for it.
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.
Ok sure, this make sense.
Someone needs to help me figure out what to do with the one test failure. |
The test failure looks like it is catching something valid-ish.
For that particular test-case, it seems like the error we're spitting out now is much less descriptive of the problem. |
I have fixed the test issue due to an less descriptive message in the test that runs Although that does work, it prevents for example to use the decorators since it will throw because parseMaybeAssign is used inside decorators as well. The best solution I found was to add in parseMaybeAssign another flag to detect when to raise the error, but I don't like it. I'm not very happy with this change. |
- Adding optional plugin `classPrivateProperties` - Adding PrivateName type identifier - Adding ClassPrivateProperty to ClassBody - Allow PrivateName in MemberExpression - Allow PrivateName as a reference - Adding tests
beef2ef
to
5d7b986
Compare
Alright this PR has been dead for way too long, and I promised @littledan I will try to land it before the next TC39 meeting :) I fixed the couple outstanding test issues and added a bunch of specific tests for private fields. There are two small details based on the feedback here and the conversation on estree #154: First is about internal naming convention for The second issue is about wether PrivateName contains in the name field a IMHO any of the previous points shouldn't be blocking this PR, otherwise we will continue to be stacked without being able to get feedback from developers and push this hopefully forward. @loganfsmyth @hzoo Can we do another pass at this PR, and unless there are other concerns, merge it? |
I'm fine with the current code. My only nitpick would be, maybe there doesn't need to be a 'liberal' parameter, as private names are always liberal. |
Codecov Report
@@ Coverage Diff @@
## master #260 +/- ##
=========================================
- Coverage 98.27% 98.18% -0.1%
=========================================
Files 22 22
Lines 3530 3572 +42
Branches 979 992 +13
=========================================
+ Hits 3469 3507 +38
- Misses 22 24 +2
- Partials 39 41 +2
Continue to review full report at Codecov.
|
@littledan suggestion done! |
I think not much if we are ok with the naming conventions - I think we should be ok with making the breaking changes if necessary. You can ignore the coverage issue if it's not making sense However (I guess this is my responsibility not yours 😄 ) wanted to be clearer about how we do proposals moving forward (meta issue). I thought that by making a "plugin" like flow/jsx do we could have independent versions of proposals. So if a breaking change is necessary (and it will probably be if we change the AST node names), how can we update the parser without requiring a major version bump? The proposal was to just make another plugin like "classPrivateProperties2" or something etc and then it would be a minor version bump in babylon itself while the transform could do a major version. #275 |
@hzoo Ready to be versioned in the future :) |
@hzoo Who else I need to bribe to get this in? :) I will keep poking! |
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 other than we aren't sure about the AST node types. I want to get someone else's feedback like @loganfsmyth @danez but I believe they are both busy atm
ast/spec.md
Outdated
```js | ||
interface PrivateName <: Expression, Pattern { | ||
type: "PrivateName"; | ||
name: string; |
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.
Still feeling like this should be Identifier
, otherwise we're diverging from the semantics that are used throughout the rest of the AST, where literal text blocks without quotes are Identifier
nodes.
I agree it's not as pretty nesting-wise, but it seems more 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.
I believe semantics are different.
It doesn't make sense to have an Identifier that has its private attribute to true, first because its only available in the scope of a ClassBody, seconds because its meaning of it is different on the spec.
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 could have been clearer, my suggestion is
interface PrivateName <: Expression, Pattern {
type: "PrivateName";
name: Identifier;
}
I'm not suggesting a private: boolean
flag on Identifier
.
Identifier
nodes in our spec are currently every case of non-keyword, non-string literals. The same way it's not name: string
on FunctionDeclaration
names or MemberExpression
properties.
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.
That I like better. @littledan What do you think?
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.
Well, thinking of unquoted property names or private field names as Identifiers isn't all that intuitive for me--they are more like strings with a prettier syntax. But if this is just an AST and the rest of the system works this way, it seems fine. I agree that these are somewhat analogous to property names, as long as they are marked specially.
src/tokenizer/index.js
Outdated
@@ -407,6 +407,13 @@ export default class Tokenizer extends LocationParser { | |||
switch (code) { | |||
// The interpretation of a dot depends on whether it is followed |
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 comment should go after the new case for 35 ("#"
).
src/parser/expression.js
Outdated
@@ -518,6 +517,13 @@ export default class ExpressionParser extends LValParser { | |||
this.takeDecorators(node); | |||
return this.parseClass(node, false); | |||
|
|||
case tt.hash: | |||
if (this.hasPlugin("classPrivateProperties")) { | |||
return this.parsePrivateName(true); |
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.
Is this a leftover true
from the removal of the "liberal" parameter?
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.
Yep will update the PR
@loganfsmyth with this we should be good to go! :) |
type: "PrivateName"; | ||
name: string; | ||
|
||
__clone(): Identifier; |
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 presumably clones to a PrivateName
and not an Identifier
.
I also don't think it should be a PatternBase
unless it's allowed to appear as a parameter.
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's only this.#a
, #a
and #a = 1
in the class body? At least from the examples.
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.
That's correct
First step to support Private Fields
Implemented grammar changes according to the spec:
classPrivateProperties
Bonus: Minor renaming and small code tweaks towards a future refactor within ClassBody parsing method.