Skip to content

Conversation

@helixbass
Copy link
Collaborator

@GeoffreyBooth @zdenko another WIP similar to #5057: this one preserves the original operator eg on/yes/true or is/==

Uses the same technique of stashing .original on a new String() wrapper around the token value. While I followed the same pattern in the node constructor of immediately calling normalizeStringObject() to unwrap to a primitive string, I ran into more cases within lexer.coffee itself where it wanted to do direct string comparisons against the token value so I had to call normalizeStringObject() in other places where it could be wanting to compare to a wrapped String()

I've wired this one up fully with Prettier/AST and am able to see the original operator in reformatted source

The only other thing I was thinking of including in this PR was preserving @ vs this? And I guess :: vs .prototype

@helixbass
Copy link
Collaborator Author

A couple other things that ultimately will need to be preserved:

  • whether the ... of a splat was prefix or postfix
  • whether a function with no params used () -> or just ->
  • whether a call used parens or not

As well as things like whether the postfix form of if/while/for was used - I've started doing this "inline" on my prettier branch but can extract into separate PR if it makes sense to, maybe I can group all the ones (like this) that don't require token data-stashing but just require passing a flag to the node constructor in certain grammar rule variants into their own PR

Copy link
Collaborator

@GeoffreyBooth GeoffreyBooth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the general idea of using String objects to sneak extra properties through the lexer is very clever. This is another workaround along the lines of my #5045 PR, but a better approach than that PR. That said, it’s awfully annoying to work with String objects as opposed to primitive strings. Could we perhaps add the extra properties onto the token objects while we’re working in the lexer, and only shift them onto this new String object at the last second before sending the data through the parser? Perhaps we could add one final new rewriter pass, that looks for extra properties on tokens and creates String objects to move them onto? And then likewise on the nodes side, perhaps the constructor of the Base class could check if value was a String object rather than a primitive string and if so, add the properties to the class and convert value back into a primitive string.

else string

exports.normalizeStringObject = (str) ->
str?.valueOf?() ? str
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do without this function. Everywhere you’re currently calling it, you could do

"#{str}"

instead. Proof:

primitiveString = 'foo'
stringObject = new String 'foo'
primitiveString is stringObject # false
primitiveString is "#{stringObject}" # true

@helixbass helixbass force-pushed the preserve-aliased-operators branch from b3f4828 to 429ab6a Compare May 13, 2018 17:21
@GeoffreyBooth GeoffreyBooth changed the base branch from master to ast June 11, 2018 04:43
@helixbass helixbass changed the title WIP: Preserve aliased operators Preserve aliased operators Jun 18, 2018
@helixbass
Copy link
Collaborator Author

@GeoffreyBooth merged ast and updated to use $1.toString() pattern in grammar

Copy link
Collaborator

@GeoffreyBooth GeoffreyBooth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, just a few minor changes.


Operation: [
o 'UNARY Expression', -> new Op $1 , $2
o 'UNARY Expression', -> new Op $1.toString(), $2, null, null, originalOperator: $1.original
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than passing null to skip arguments, we should pass undefined, as a general rule. Since only undefined allows function parameter default values to be set.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to undefined (in all the updated rules)

src/nodes.coffee Outdated
@operator in ['<', '>', '>=', '<=', '===', '!==']

invert: ->
if @isIn()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until I read farther down, I thought this meant if is inverted.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya this is a tricky name - updated to @isInExpression() but even that could be misinterpreted. @isAnInExpression(), though less ambiguous, looks weird to me

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well doesn’t in refer to the operator? And this is in the Op class, so shouldn’t we call this isInOperator?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GeoffreyBooth sure updated to isInOperator()

Copy link
Collaborator Author

@helixbass helixbass left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GeoffreyBooth updated per your comments

src/nodes.coffee Outdated
@operator in ['<', '>', '>=', '<=', '===', '!==']

invert: ->
if @isIn()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya this is a tricky name - updated to @isInExpression() but even that could be misinterpreted. @isAnInExpression(), though less ambiguous, looks weird to me


Operation: [
o 'UNARY Expression', -> new Op $1 , $2
o 'UNARY Expression', -> new Op $1.toString(), $2, null, null, originalOperator: $1.original
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to undefined (in all the updated rules)

@GeoffreyBooth GeoffreyBooth merged commit af82c1f into jashkenas:ast Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants