Skip to content
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

Added support for record and tuple syntax. #10865

Merged
merged 18 commits into from Mar 16, 2020

Conversation

rickbutton
Copy link
Contributor

Q                       A
Fixed Issues?
Patch: Bug Fix?
Major: Breaking Change?
Minor: New Feature? 👍
Tests Added + Pass? 👍
Documentation PR Link
Any Dependency Changes?
License MIT

TC39 Proposal Info:
Champion: @rricard @rickbutton
Spec Repo: https://github.com/tc39/proposal-record-tuple
Slides (Stage 1 at Oct 2019 TC39 Meeting): https://button.dev/talks/records-and-tuples-tc39-october-2019.pdf

This PR adds support for both the hash style #{ a: 1 } and the bar style {| a: 1 |} record and tuple syntax to babel-parser under the option recordAndTuple.{hash/bar}. I've also added a new plugin babel-plugin-syntax-record-and-tuple for enabling the parser option.

This does not include a plugin for transforming the syntax, we intend to iterate on a transform and polyfill separately for now, similar to the approach taken for BigInt.

This is my first babel PR, so please let me know if I'm missing something!

Copy link

@littledan littledan left a comment

Choose a reason for hiding this comment

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

At a glance, looks like you added the "hash": true option to use #{ } syntax, and otherwise, the {| |} syntax is used. I'd suggest that you instead make a mandatory parameter which is string-valued, like "syntax": "bar" vs "syntax": "hash". This could be more future-proof.

@rickbutton
Copy link
Contributor Author

@littledan the changes to the tokenzier check for the existence of the bar config options in order to tokenize the {| and |} tokens, the parser itself will consume those tokens regardless of the options provided (but more defense in depth would make sense here, I should probably check for the option at both levels).

I'm also not opposed to switching to syntax: "bar" instead of bar: true so that one or the other must be chosen.

@nicolo-ribaudo nicolo-ribaudo self-assigned this Dec 14, 2019
@nicolo-ribaudo nicolo-ribaudo added pkg: parser PR: New Feature 🚀 A type of pull request used for our changelog categories labels Dec 14, 2019
@@ -1046,11 +1060,27 @@ export default class ExpressionParser extends LValParser {
this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody;
return this.finishNode(node, "ArrayExpression");
}
case tt.braceBarL:
case tt.braceHashL: {
Copy link
Contributor

Choose a reason for hiding this comment

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

should assert recordAndTuple presents otherwise it is a syntax error.

this.expectPlugin("recordAndTuple");

Can you add a test case when recordAndTuple is not enabled?

aliases: ["Expression"],
fields: {
syntaxType: {
validate: assertValueType("string"),
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's be more explicit

assertOneOf("bar", "hash")

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure that this information should be there. This seems like syntax-specific information, not related to the AST.
It's similar to how the presence or absence of a semicolon is not represented in the AST.

I would prefer it as an option in @babel/generator, which should throw when printing a RecordExpression node if that option is not specified.
@babel/plugin-syntax-record-and-tuple would set both the parser option and the generator option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nicolo-ribaudo do you mean an additional property on the node specifically for the generator to use to pick syntax to output? If so, why wouldn't you just use the existing syntaxType property?

Copy link
Contributor

@JLHwung JLHwung Dec 17, 2019

Choose a reason for hiding this comment

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

The generator options are independent on AST. By doing so we can have the AST agnostic about the syntax types, which is semantically equivalent.

You can add a new format option here

and reference this option in the generator via this.format.recordTupleSyntaxType.

In the syntax-* you can mutate the generator options via options.generatorOpts.recordTupleSyntaxType.

parserOpts.plugins.push([
"recordAndTuple",
{
hash: options.hash,
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer to expose syntaxType to the user land, because

  • When either syntax variants is accepted, we can make that as default. We have similar design on pipeline-operator.
  • We don't have to process option precedence issues like hash: false, bar: false and hash: true, bar: true.
  • More open to competing proposals, if any

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

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

Could you also add a parser test with Flow like this?

var a: {| x: number |} = {| x: 2 |}

@@ -93,9 +93,13 @@ export const types: { [name: string]: TokenType } = {

// Punctuation token types.
bracketL: new TokenType("[", { beforeExpr, startsExpr }),
bracketHashL: new TokenType("#[", { beforeExpr, startsExpr }),
bracketBarL: new TokenType("{|", { beforeExpr, startsExpr }),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
bracketBarL: new TokenType("{|", { beforeExpr, startsExpr }),
bracketBarL: new TokenType("[|", { beforeExpr, startsExpr }),

@rickbutton
Copy link
Contributor Author

rickbutton commented Dec 18, 2019

I've pushed a set of commits that I think addresses all of your concerns so far:

  • I've moved the parser options to use syntaxType: "hash" instead of hash: true cc @littledan @JLHwung
  • added checks to the parser to check for the recordAndTuple plugin instead of relying only on the tokenizer to pick up the error (and added tests to check for missing plugin + invalid syntaxType
  • Moved the syntaxType node property into an option to the generator (and added some tests for this)
  • added a test for flow fixed type syntax with record/tuple bar syntax cc @nicolo-ribaudo

Let me know if I missed anything! Thank you for the super detailed + quick reviews!

edit: looks like i need to account for some lint errors as well, I will follow up on those soon.

@rickbutton
Copy link
Contributor Author

Just pushed a rebase, looks like the standalone tests pass, but e2e and coverage are failing, but they look like unrelated failures. Any ideas? @nicolo-ribaudo @JLHwung

@JLHwung
Copy link
Contributor

JLHwung commented Jan 9, 2020

The CI failure is unrelated. Did you fetch the upstream? If you rebase on the latest upstream the CI error should be gone.

@nicolo-ribaudo nicolo-ribaudo added this to the v7.9.0 milestone Jan 9, 2020
@rickbutton
Copy link
Contributor Author

@JLHwung yep, fetched upstream and rebased. parent commit of this PR is current HEAD of babel/babel#master 8fd532d

@JLHwung
Copy link
Contributor

JLHwung commented Jan 10, 2020

@rickbutton Can you rebase again? The CI error has been fixed in recent commits.

@rickbutton
Copy link
Contributor Author

@JLHwung rebased again, everything is green! (minus test262). Thank you!

endToken = "}";
} else {
throw new Error(
`${this.format.recordAndTupleSyntaxType} is not a valid recordAndTuple syntax type`,
Copy link
Member

Choose a reason for hiding this comment

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

This error message should mention the option name, and also be easy to understand when the option is not provided. WDYT about this?

      `The "recordAndTupleSyntaxType" generator option must be either "bar" or "hash" (${JSON.stringify(this.format.recordAndTupleSyntaxType)} received).`,

Also, please add a test for an invalid value of the option and for when the option is not set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nicolo-ribaudo pushed a change to include this new message, and added the tests you mentioned.

The babel-generator test runner doesn't support the standard throws option in options.json (looks like it isn't using the same infra as everything else). I took the liberty of adding support for throws to the package's test suite, let me know if this should exist somewhere else.

const start = this.state.pos;
this.state.pos += 1;

let ch = this.input.charCodeAt(this.state.pos);
if (ch !== charCodes.exclamationMark) return false;
Copy link
Member

Choose a reason for hiding this comment

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

What is this change for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved this because it will incorrectly eat a # character if the next character isn't a !, I think that the this.state.pos should only be advanced if it is actually going to be an interpreter directive. (This happens if the first line of the file is #{}

packages/babel-parser/src/types.js Outdated Show resolved Hide resolved
@@ -0,0 +1,4 @@
{
"plugins": [["recordAndTuple", { "syntaxType": "invalid" }]],
"throws": "Unexpected token (1:1)"
Copy link
Member

Choose a reason for hiding this comment

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

Here we should throw an error similar to @babel/generator's for the invalid option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, how about this? (used the pipelineOperator option checks for a reference here).

'recordAndTuple` requires `syntaxType` option whose value should be one of: 'hash', 'bar'

Copy link
Member

Choose a reason for hiding this comment

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

Yes 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done :)

@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Jun 30, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: parser PR: New Feature 🚀 A type of pull request used for our changelog categories PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release Spec: Record Tuple
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants