Skip to content

Conversation

@harlanhaskins
Copy link
Contributor

@harlanhaskins harlanhaskins commented Aug 2, 2017

This patch is an initial implementation of the Swift libSyntax API. It
aims to provide all features of the C++ API but exposed to Swift.

It currently resides in tools/ and will likely exist in a
molten state for a while.

Tasks remaining:

  • Get the tests passing
  • Write more tests
  • Figure out where this should live (It lives in tools/)
  • Review API for Swiftiness.

@nkcsgexi
Copy link
Contributor

nkcsgexi commented Aug 2, 2017

woot!

@felix91gr
Copy link
Contributor

I get the reference of your commit message 👀
Also, what is libSyntax for? I couldn’t find such a library for C++

@harlanhaskins
Copy link
Contributor Author

@felix91gr libSyntax is a library in the Swift compiler that provides a full source-preserving Syntax tree that can be easily transformed and re-printed as a file.

You can find more info about the C++ side of libSyntax here: https://github.com/apple/swift/tree/master/lib/Syntax

@felix91gr
Copy link
Contributor

That’s awesome! Thank you for explaining 😊

@tiferrei
Copy link

tiferrei commented Aug 3, 2017

So, would I be able to use this to get an AST from a file, almost like Clang's libTooling?

@harlanhaskins
Copy link
Contributor Author

@tiferrei You'll be able to call Syntax.parse(file) and get a SourceFileSyntax that you can walk, transform, and re-print to a file if you want.

Copy link
Contributor

@nkcsgexi nkcsgexi left a comment

Choose a reason for hiding this comment

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

Looks great! some minor comments/question in line.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to assert the validity of index here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Technically it'll fail in the stdlib but it wouldn't hurt to add a more descriptive error message here 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add some doc-comment that this is the access path from the root to a specific node?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking more about this, in our design, should the meaning of the identifier be transparent to the end users? Do you think clients will misuse them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should be an implementation detail and not exposed to clients.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's good. Thanks for explaining!

Copy link
Contributor

Choose a reason for hiding this comment

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

What does this if statement do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(this should go in a comment above this if statement)

If the token doesn't have an exact text specified, then the case will have a single associated value of type String. This is for identifier, spacedBinaryOperator, unspacedBinaryOperator, prefixOperator, postfixOperator, integerLiteral, floatingLiteral, and stringLiteral

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we gyb over TriviaPiece to shorten these above two switch statements?

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'm wary of gybing Trivia just because it's a static set of values and we don't anticipate it changing.

Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, i think gybing reduces logic duplication and avoids some errors introduced by copy-n-paste.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's fair! I can gyb this, no problem.

Copy link
Contributor

Choose a reason for hiding this comment

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

You could use a method that takes the key+count if you think it's worth factoring this. I don't think gyb'ing is the right answer for this. Gyb adds a large cognitive burden to any code that uses it, so it has to have a big benefit to be worth it IMO.

Copy link
Contributor

Choose a reason for hiding this comment

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

For simple logic like this, I don't think gybing will harm readability and it makes sure we cover all cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

There is no default in this switch, so the language already ensures we cover all cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

Conceptually, I agree that gyb can be hard to read than actual code; but based on the context here, also since we are using gyb already for other enums in this patch, i still prefer we generate the switch to be consistent. Readability won't suffer because of its simple logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we generate these factory methods too?

Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly, I disagree with gyb'ing this. It's much easier to read as plain source code and the benefit is very small.

Copy link
Contributor

Choose a reason for hiding this comment

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

Using gyb here doesn't harm readability either. The logic is straightforward.

Copy link
Contributor

Choose a reason for hiding this comment

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

What's the outcome of the validation? Should it throw or return error if validation fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just debug validation to ensure that the things generated from SyntaxFactory/SyntaxBuilders in the tests hold the right structure. This validation doesn't happen in Release builds because the only way to construct one of these is through the Factory and Builders, and they're meant to guarantee you a valid structure.

Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds like an Equatable conformance. Doesn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah! Yeah, I had originally structured this with protocols, and didn't want to enforce Equatable conformance because of the Self requirement, but since this is a class hierarchy I can make this a conformance.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would this work?

extension Syntax : Equatable {
  public static func == (lhs: Self, rhs: Self) -> Bool {
    return lhs.data === rhs.data
  }
}

Copy link
Contributor

@benlangmuir benlangmuir left a comment

Choose a reason for hiding this comment

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

Overall looking great, thanks Harlan!

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this trying to say that .missing does not imply "implicit", but instead might be a required syntax element that was omitted?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, I can clarify the comment a bit.

Copy link
Contributor

@benlangmuir benlangmuir Aug 3, 2017

Choose a reason for hiding this comment

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

I'm curious why not a random access collection?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not a random access collection would be something like a Stack or a Queue, where you can only work on a specific region of the Data Structure. Contrasting with an Array or a List in that regard.

Copy link
Contributor

Choose a reason for hiding this comment

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

D'oh, edited my comment: I was trying to ask why this is not a random access collection, since it seems like the underlying abstraction would support it. That said, I'm really just curious, no need to change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The underlying abstraction totally does support it. It's currently bound to Collection but I'll conform it to RandomAccessCollection instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we end up in a situation where the parent goes away without the syntax data being "invalid" in some send, or could this be unowned?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

parent is optional -- nodes that are the root node have no parent. We can't have an unowned optional ☹️

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure how often you'd have more than one backtick in a row in source code, but I guess the Int is free in this representation 😉

Copy link
Contributor

Choose a reason for hiding this comment

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

See TextOutputStreamable protocol.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's absolutely what I was missing here. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, you don't want to do this ;-) (I looked inside AnyIterator and I saw things in there). Writing a special Iterator struct for SyntaxChildren is just a few more lines of code, but avoids an unnecessary allocation.

Copy link
Contributor

@moiseev moiseev Aug 3, 2017

Choose a reason for hiding this comment

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

Based on the methods in the class itself, it can also conform (I guess) to MutableCollection and RangeReplaceableCollection and potentially acquire a more familiar API for things like removingLast etc...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can't conform to MutableCollection because the nodes themselves are immutable.

@harlanhaskins
Copy link
Contributor Author

@moiseev This shouldn't live in stdlib/internal/ -- where should it live? Also I need to figure out what CMake flags this needs. Currently it's using the same flags as SwiftExperimental.

@moiseev
Copy link
Contributor

moiseev commented Aug 3, 2017

/cc @zisko for the CMake question above.
As for the location, let's leave it where it is and move if the need arises (or we come up with a better location for it).

@zisko
Copy link
Contributor

zisko commented Aug 3, 2017

At the place it is now, the CMake LGTM.

@harlanhaskins harlanhaskins force-pushed the he-protecc-but-he-also-syntacc branch 2 times, most recently from d31a71e to e43a933 Compare August 4, 2017 22:48
@harlanhaskins
Copy link
Contributor Author

@swift-ci please smoke test

@shahmishal
Copy link
Member

@swift-ci please python lint

2 similar comments
@harlanhaskins
Copy link
Contributor Author

@swift-ci please python lint

@shahmishal
Copy link
Member

@swift-ci please python lint

@harlanhaskins
Copy link
Contributor Author

@swift-ci please smoke test

@harlanhaskins harlanhaskins force-pushed the he-protecc-but-he-also-syntacc branch from 9c96b21 to 4f019ba Compare August 7, 2017 18:57
Copy link

Choose a reason for hiding this comment

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

Typo: noe -> node

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks!

@harlanhaskins harlanhaskins force-pushed the he-protecc-but-he-also-syntacc branch 2 times, most recently from 34faf91 to 19c275b Compare August 14, 2017 18:24
This patch is an initial implementation of the Swift libSyntax API. It
aims to provide all features of the C++ API but exposed to Swift.

It currently resides in SwiftExperimental and will likely exist in a
molten state for a while.
@harlanhaskins harlanhaskins force-pushed the he-protecc-but-he-also-syntacc branch from 19c275b to 4a8b360 Compare August 14, 2017 18:25
@harlanhaskins harlanhaskins changed the title [WIP] [Syntax] [Do Not Merge] Swift libSyntax API [Syntax] Swift libSyntax API Aug 14, 2017
@harlanhaskins
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 4a8b360

@harlanhaskins
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 4a8b360

@harlanhaskins
Copy link
Contributor Author

@swift-ci please test

@harlanhaskins
Copy link
Contributor Author

Not sure what happened with that last failure.

@harlanhaskins
Copy link
Contributor Author

@nkcsgexi @benlangmuir @moiseev Good to merge?

@nkcsgexi
Copy link
Contributor

I've no more comments! LGTM!

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - fd45799

@benlangmuir
Copy link
Contributor

👍

@harlanhaskins
Copy link
Contributor Author

That "Build failed" comment...doesn't make sense? It actually passed. 🤔

@harlanhaskins
Copy link
Contributor Author

@harlanhaskins harlanhaskins merged commit ade67ca into swiftlang:master Aug 14, 2017
maldahleh pushed a commit to maldahleh/swift that referenced this pull request Oct 26, 2020
* Create Swift libSyntax API

This patch is an initial implementation of the Swift libSyntax API. It
aims to provide all features of the C++ API but exposed to Swift.

It currently resides in SwiftExperimental and will likely exist in a
molten state for a while.

* Only build SwiftSyntax on macOS
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.

10 participants