-
Notifications
You must be signed in to change notification settings - Fork 2k
AST flag/API option; generic AST output for all nodes #5044
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
Conversation
…ST; starting point for toPlainObject method on node classes to return the serializable plain JavaScript object representation of each node
It's nice to see this being developed in a side PR — I'm not terribly optimistic about how easy it's going to be to get prettier to format CoffeeScript. But looks like a fine start! My only comment is that the |
Good luck, have fun; as they say! Happy to see you try and tackle this one |
Um, I’m hoping to not tackle it alone! Help most welcome! Prettier can handle oddball formats like CSS and Markdown, so I would think it could handle CoffeeScript. But regardless that’s a whole separate effort. I just want to get a good AST out first, so that others can tackle integrations. JS2Coffee already has code that converts an AST into CoffeeScript source, so clearly it’s possible to do. |
I think there’s a fairly easy way to track our progress toward a “complete” AST. So the Babel AST spec lists every one of Babel’s AST token types and properties. For example: interface RegExpLiteral <: Literal {
type: "RegExpLiteral";
pattern: string;
flags: string;
} Almost all of our node types have corresponding types in Babel’s AST spec. We could compare the AST output for our node against its counterpart in the Babel AST, and if both contain the same properties, that node type is complete. So for Comparing node types also has the advantage that we don’t need a code generation method (like Prettier) built yet. All we need is a “kitchen sink” input file that has at least one of every node type, so it generates and AST that we can compare against Babel’s. @zdenko @helixbass |
Some context about prettier, the way it works is the following: from an AST you generate an intermediate representation ( https://github.com/prettier/prettier/blob/master/commands.md ) that describes where things should break. Since the CoffeeScript AST is different than the JavaScript AST, you're likely going to have to rebuilt all that part from scratch. If you use prettier, you're going to get a lot of infrastructure for free:
I didn't realize at the time but there's actually a bunch of work outside of just the core formatter that is needed to make one successful. So, we've been trying to make a plugin system where if you want to add a new language, you just have to build the printer for that language and you get the rest for free. |
Thanks @vjeux. One of our maintainers is @lydell, so presumably we’ll have some help when it comes to the Prettier side of things 😄 One thing I’m concerned by is the idea of line breaking CoffeeScript source code in general. I know that intelligent line breaks was the original purpose of Prettier, but I think it’s fair to say that Prettier has evolved from that to now function as a best practices code reformatter in general. Since CoffeeScript is whitespace dependent, unlike JavaScript, it’s common to assume a soft word wrap and never break lines of code for the purpose of keeping under a column limit. Certainly people write objects and arrays in multiline style instead of inline when the inline version starts to get long and unwieldy, for example, but using the line continuation symbol |
Prettier itself doesn't enforce any of those, this is the person writing the ast -> IR that has to make those decisions. The IR allows you to express what happens if something goes over 80 columns. It may seem that enforcing based on the 80-column rule is not ideal but in practice, people tend to write code that way. For example if you look at the files in coffeescript/src/, they tend to fit 80 columns. Now, this is only one input around when to break. For example in prettier we always break for The strength of prettier infrastructure is that it gives you (IMO) the right primitives on-top of which you can build a solid pretty printer tailored for your language and the way you want code to look like. |
@helixbass Is this safe to merge into the I was thinking that if this were in, then we at least have a simple way of generating AST output that we can refer to as we merge more and more PRs in. |
@GeoffreyBooth sure, there are things that would conflict with the |
Building off of #4984. The major prerequisite to support for CoffeeScript in Prettier, or better ecosystem tooling in general (a new JS2Coffee, etc.) is being able to generate a abstract syntax tree, or AST. This is a JSON output (or in Node, a plain JavaScript object output) representation of all the nodes that the compiler would be generating JavaScript source code for. Once we have an AST, it can be passed into Prettier for Prettier to generate pretty-formatted CoffeeScript; into CoffeeLint for linting; and so on. The intent of this PR is to focus only on the “generate an AST” part, leaving further integrations for future PRs.
This PR aims to update the CoffeeScript compiler to generate such an AST. The current CLI already has a
--nodes
flag that prints debugging info about all the nodes; I’m updating that to produce the output as JSON instead. The Node CLI will take anodes
option to return the same thing.This PR will be WIP until all the nodes have complete details about them, such that Prettier has enough to work with to generate new CoffeeScript source. I’m treating that as the initial benchmark for completeness; if other tools require yet more detail, the additional details can be added in future PRs.
To try out generating an AST, check out this branch and run:
./bin/coffee --nodes --eval 'answer = 42'
For syntax-highlighted output, or a longer input, create a
test.coffee
at the root of the repo with whatever CoffeeScript code you want to see an AST of, then run:You should see pretty-printed JSON like this:
This is the same output as the current CLI’s
--nodes
, in JSON form, with:type
, which is how the Babel AST has themFor nodes as simple as
NumberLiteral
, this is all the detail we need. But for complicated nodes likeClass
, a lot more work will be required. Basically, every node class that has acompileNode
method will need a similarcompileObject
method; the former generates a JavaScript output string, and the latter will generate AST objects. Another issue that needs tackling, perhaps in a separate PR, is preserving data from the lexer that doesn’t make it into the node classes but that we need to produce a complete AST object. I’m hoping we can use this branch to track this work, and I’m happy to invite anyone to my repo who would like to contribute.