yanagiba / swift-ast

A Swift AST parser and visitor that is written in Swift.
Apache License 2.0
355 stars 40 forks source link

Swift Abstract Syntax Tree

swift-ast master swift-lint master swift-transform pending

Travis CI Status codecov Swift 5.1 Swift Package Manager Platforms License

Swift Abstract Syntax Tree is an initiative to parse Swift Programming Language in Swift itself. The output of this utility is the corresponding Abstract Syntax Tree (AST) of the source code.

The AST produced in this tool is intended to be consumed in various scenarios. For example, source-to-source transformations like swift-transform and linting tools like swift-lint.

Refactoring, code manipulation and optimization can leverage this AST as well.

Other ideas could be llvm-codegen or jvm-codegen (thinking about JSwift) that consumes the AST and converts them into binary or bytecode. I have some proof-of-concepts, llswift-poc and jswift-poc, respectively. If you are interested in working on the codegens, send me email ryuichi@yanagiba.org.

Swift Abstract Syntax Tree is part of Yanagiba Project. Yanagiba umbrella project is a toolchain of compiler modules, libraries, and utilities, written in Swift and for Swift.


A Work In Progress

The Swift Abstract Syntax Tree is still in active development. Though many features are implemented, some with limitations.

Pull requests for new features, issues and comments for existing implementations are welcomed.

Please also be advised that the Swift language is under rapid development, its syntax is not stable. So the details are subject to change in order to catch up as Swift evolves.

Requirements

Installation

Standalone Tool

To use it as a standalone tool, clone this repository to your local machine by

git clone https://github.com/yanagiba/swift-ast

Go to the repository folder, run the following command:

swift build -c release

This will generate a swift-ast executable inside .build/release folder.

Adding to swift Path (Optional)

It is possible to copy the swift-ast to the bin folder of your local Swift installation.

For example, if which swift outputs

/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Then you can copy swift-ast to it by

cp .build/release/swift-ast /Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-ast

Once you have done this, you can invoke swift-ast by calling swift ast in your terminal directly.

Embed Into Your Project

Add the swift-ast dependency to Package.swift:

// swift-tools-version:5.0

import PackageDescription

let package = Package(
  name: "MyPackage",
  dependencies: [
    .package(url: "https://github.com/yanagiba/swift-ast.git", from: "0.19.9")
  ],
  targets: [
    .target(name: "MyTarget", dependencies: ["SwiftAST+Tooling"]),
  ],
  swiftLanguageVersions: [.v5]
)

Usage

Command Line

Simply append the path of the file to swift-ast. It will dump the AST to the console.

swift-ast path/to/Awesome.swift

Multiple files can be parsed with one call:

swift-ast path1/to1/foo.swift path2/to2/bar.swift ... path3/to3/main.swift

CLI Options

By default, the AST output is in a plain text format without indentation nor color highlight to the keywords. The output format can be changed by providing the following option:

In addition, -github-issue can be provided as the first argument option, and the program will try to generate a GitHub issue template with pre-filled content for you.

Use AST in Your Code

Loop Through AST Nodes

import AST
import Parser
import Source

do {
  let sourceFile = try SourceReader.read(at: filePath)
  let parser = Parser(source: sourceFile)
  let topLevelDecl = try parser.parse()

  for stmt in topLevelDecl.statements {
    // consume statement
  }
} catch {
  // handle errors
}

Traverse AST Nodes

We provide a pre-order depth-first traversal implementation on all AST nodes. In order to use this, simply write your visitor by conforming ASTVisitor protocol with the visit methods for the AST nodes that are interested to you. You can also write your own traversal implementations to override the default behaviors.

Returning false from traverse and visit methods will stop the traverse.

class MyVisitor : ASTVisitor {
  func visit(_ ifStmt: IfStatement) throws -> Bool {
    // visit this if statement

    return true
  }
}
let myVisitor = MyVisitor()
let topLevelDecl = MyParse.parse()
myVisitor.traverse(topLevelDecl)

Development

Build & Run

Building the entire project can be done by simply calling:

make

This is equivalent to

swift build

The dev version of the tool can be invoked by:

.build/debug/swift-ast path/to/file.swift

Running Tests

Compile and run the entire tests by:

make test

Contact

Ryuichi Sai

License

Swift Abstract Syntax Tree is available under the Apache License 2.0. See the LICENSE file for more info.