Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fliskdata/swift-ast

Repository files navigation

@flisk/swift-ast

Swift AST parsing in JavaScript via WebAssembly (WASM), powered by SwiftSyntax + SwiftParser.

NPM version Tests

Introduction

This package compiles SwiftSyntax + SwiftParser to a WASI module and exposes a simple JavaScript API to parse Swift source into a compact JSON AST. On top of that, it provides a lightweight analyzer (inspired by Ruby Prism’s ergonomics) to:

  • extract declarations (functions, methods, classes, structs, enums, variables)
  • find and inspect function/method calls (with callee chain and base identifier)
  • follow simple identifier references within lexical scope
  • surface naive type hints where present (e.g., let x: Int)

It’s designed for use in program analysis pipelines like @flisk/analyze-tracking and can also be used as a CLI with npx.

Quick Start (CLI)

Run without installing:

npx @flisk/swift-ast /path/to/file.swift

This prints the parsed AST JSON to stdout.

Install

npm i @flisk/swift-ast

Programmatic API

Parse to JSON AST

import { parseSwiftAst } from '@flisk/swift-ast';

const source = `
struct Foo {
  let x: Int
  func bar(_ y: Int) -> Int { x + y }
}
`;

const ast = await parseSwiftAst(source);
console.dir(ast, { depth: null });

AST shape (simplified):

{
  "root": 0,
  "nodes": [
    { "kind": "SourceFileSyntax", "range": { "start": {"offset":0}, "end": {"offset":123} } },
    { "kind": "StructDeclSyntax", "name": "Foo", "range": { /* ... */ } },
    { "kind": "FunctionDeclSyntax", "name": "bar", "range": { /* ... */ } }
  ],
  "edges": [[0,1],[1,2]]
}

There’s also a convenience for file-based parsing:

import { parseSwiftFile } from '@flisk/swift-ast';
const astFromFile = await parseSwiftFile('path/to/file.swift');

AST analyzer

import { analyzeAst } from '@flisk/swift-ast';

const analysis = analyzeAst(ast, source);

// All declarations by name
console.log([...analysis.byName.keys()]);

// All calls to a function/method named "track"
const calls = analysis.findCallsByName('track');
for (const c of calls) {
  console.log({
    name: c.name,
    receiver: c.receiver,
    base: c.baseIdentifier,
    chain: c.calleeChain,
    args: analysis.getCallArgs(c.id)
  });
}

// Resolve a simple name to its symbol (best-effort)
const symbol = analysis.resolveNameAt('bar', /*offset*/ 0);
console.log(symbol?.kind, symbol?.name);

Analyzer return type (high-level):

type Analysis = {
  symbols: Map<number, SymbolInfo>;
  calls: CallInfo[];
  refs: RefInfo[];
  byName: Map<string, number[]>;
  findCallsByName(name: string): CallInfo[];
  resolveNameAt(name: string, offset: number): SymbolInfo | undefined;
}

Notes:

  • Syntax-driven only (no full type-checker). Name resolution is lexical and best-effort.
  • Exposes helpers to ease tracking analysis: labeled args, dictionary extraction, enclosing symbol lookup, const string collection, and callee text/chain utilities.

CLI usage

# Print AST JSON
npx @flisk/swift-ast /path/to/file.swift

# With a local install
swift-ast /path/to/file.swift > ast.json

Recipes

  • List all class/struct names
const decls = [...analysis.symbols.values()].filter(s => s.kind === 'class' || s.kind === 'struct');
console.log(decls.map(d => d.name));
  • Find all callsites of a specific API (e.g., analytics)
const hits = analysis.findCallsByName('track');
for (const call of hits) {
  // receiver could be an instance, a type, or omitted
  console.log(`${call.receiver ? call.receiver + '.' : ''}${call.name} at ${call.range.start.line}:${call.range.start.column}`);
}
  • Get naive type info for variables
const vars = [...analysis.symbols.values()].filter(s => s.kind === 'variable');
for (const v of vars) {
  console.log(v.name, '::', v.typeAnnotation ?? '(inferred)');
}

Environment

  • Node >= 18 (uses built-in node:wasi).
  • No native Swift toolchain required at runtime; the Wasm binary ships with the npm package.

Development (for contributors)

  1. Install Swift via swiftly
  2. Install Swift 6.2: swiftly install 6.2
  3. Select Swift 6.2: swiftly use 6.2
  4. Install Swift SDK for WASI:
swift sdk install https://download.swift.org/swift-6.2-release/wasm/swift-6.2-RELEASE/swift-6.2-RELEASE_wasm.artifactbundle.tar.gz --checksum fe4e8648309fce86ea522e9e0d1dc48e82df6ba6e5743dbf0c53db8429fb5224
  1. Run swift sdk list and ensure that SWIFT_SDK_ID is set to the appropriate SDK.
  2. Build the WASM binary: npm run build
  3. Run tests: npm test

Note: SwiftSyntax version is tied to the Swift toolchain used to build the WASM. If you rebuild locally, ensure a matching swift-syntax tag for your toolchain.