-dump-ast
のParserの部分を独立させて、現行の環境で動くようにしました。よかったら参考にしてください。 https://github.com/kishikawakatsumi/SwiftAST 使い方は swift-ast parse [FILE_PATH_TO_PARSE] -buildCommand [BUILD_COMMAND_TO_BUILD_THE_PROJECT]
例えば path/to/swift-ast parse "./Framework/Sources/SpreadsheetView.swift" -buildCommand xcodebuild -scheme SpreadsheetView
や、 path/to/swift-ast parse ./Lib/KeychainAccess/Keychain.swift -buildCommand xcodebuild -scheme KeychainAccess -sdk iphonesimulator -destination "name=iPhone Xs,OS=12.2"
です。 以下、思い出したことを書きます。 (edited)swift -dump-ast
produces - kishikawakatsumi/SwiftAST-dump-ast
は全ての型が解決しているので原則としてコンパイルが成功する必要があります。簡単な(依存関係がない)ファイルなら簡単ですが、他のファイルやモジュールを参照している場合は同じモジュールのファイルの場合はそれを一緒に渡す、別モジュールの場合はそれを先にビルドする必要があります。 なので、時間は余計にかかるのですが、 https://github.com/kishikawakatsumi/SwiftAST/blob/master/Sources/SwiftAST/XcodebuildTool.swift#L37-L42 のように swift -dump-ast
を実行する前にまずビルドしています。 ビルドエラーが起こる状態で swift -dump-ast
を実行した場合、単に失敗するか不完全なASTが出力されます。 swift -dump-ast
の出力自体が謎であるのにさらに不完全なASTを相手にするのは無理なので、最初のキモは確実にビルドできるパラメータを組み立てることです。 (edited)swift -dump-ast
produces - kishikawakatsumi/SwiftASTconstructor_decl
や brace_stmt
assign_expr
といったノードの種類がよくわからないことと、それぞれがどういう属性を持っているかが当然Undocumentedでよくわからないところで、Parse自体はそれほど難しくないはず(目的にもよりますが)です。private func process(sourceFile: URL, verbose: Bool = false) throws -> AST { let arguments = buildArguments(source: sourceFile) let rawAST = try dumpAST(arguments: arguments) let tokens = tokenize(rawAST: rawAST) let node = lex(tokens: tokens) let root = parse(node: node) return root }
dumpAST(arguments:)
はswift -dump-ast
を実行します。 得られたテキストをtokenize(rawAST:)
でトークンに分割して、lex(tokens:)
でツリー構造を作ります。 最後にparse(node:)
でツリー構造のデータに意味を持たせます。swift -dump-ast
produces - kishikawakatsumi/SwiftAST-dump-ast
の出力はS式のように見えるのでカッコを頼りにしたくなりますが、別の意味のカッコが普通に("
や'
に囲まれることなく)出てくるのでそれは難しいです。 代わりに行とインデントを使います。func tokenize(source: String) -> [ASTToken] { var lines = [String]() for line in source.split(separator: "\n", omittingEmptySubsequences: false) { let trimmed = line.trimmingCharacters(in: .whitespaces) if trimmed.hasPrefix("(inherited_conformance") || trimmed.hasPrefix("(normal_conformance") || trimmed.hasPrefix("(abstract_conformance") || trimmed.hasPrefix("(specialized_conformance") || trimmed.hasPrefix("(assoc_type") || trimmed.hasPrefix("(value req") || !trimmed.hasPrefix("(") { continue } lines.append(String(line)) } ...
-dump-ast
の出力をまず行ごとに分割します。このとき、いくつかのプロジェクトでノイズになる行(実行コードには関係なく見える上にインデントがおかしい)が発見されたのでそれを取り除いています。indent
(先頭の空白)、symbol
(シングルクオートで囲まれた文字列)、string
(ダブルクオートで囲まれた文字列)、token
(それ以外の文字列や記号)に分類しました。 class ASTToken { enum TokenType { case token case symbol case string case indent(Int) }