Documentation and cleanup

+7 -7
Sources/PterodactylBuild/BuildContext.swift
···
extension LLBCASFileTree {
static func load<X: FXKey>(id: LLBDataID, in ctx: BuildContext<X>) async throws -> LLBCASFileTree {
-
try await load(id: id, from: ctx.db, ctx.context).get()
}
-
func remove<X: FXKey>(path: AbsolutePath, in ctx: BuildContext<X>) async throws -> LLBCASFileTree {
-
try await remove(path: path, in: ctx.db, ctx.context).get()
-
}
-
func traverse<X: FXKey>(root: AbsolutePath, in ctx: BuildContext<X>, _ callback: (AbsolutePath, LLBDataID, LLBDirectoryEntry) async throws -> Void) async throws {
-
try await traverse(root: root, in: ctx.db, ctx.context, callback)
-
}
}
···
extension LLBCASFileTree {
static func load<X: FXKey>(id: LLBDataID, in ctx: BuildContext<X>) async throws -> LLBCASFileTree {
+
try await load(id: id, from: ctx.db, ctx.context).get()
}
+
func remove<X: FXKey>(path: AbsolutePath, in ctx: BuildContext<X>) async throws -> LLBCASFileTree {
+
try await remove(path: path, in: ctx.db, ctx.context).get()
+
}
+
func traverse<X: FXKey>(root: AbsolutePath, in ctx: BuildContext<X>, _ callback: (AbsolutePath, LLBDataID, LLBDirectoryEntry) async throws -> Void) async throws {
+
try await traverse(root: root, in: ctx.db, ctx.context, callback)
+
}
}
+5 -2
Sources/PterodactylBuild/Keys/Blob/ParseDocument.swift
···
struct ParseDocument: BuildKey {
let blobId: LLBDataID
-
typealias ValueType = PterodactylSyntax.SyntaxTree
static let versionDependencies: [any FXVersioning.Type] = [ReadContents.self, Tokenise.self]
···
let tokens = try await ctx.request(Tokenise(blobId: blobId))
var parser = Parser(source: code, tokens: tokens)
PterodactylSyntax.Document.parse(&parser)
-
return parser.tree
}
}
}
···
struct ParseDocument: BuildKey {
let blobId: LLBDataID
+
struct ValueType: Codable, FXValue {
+
let tree: PterodactylSyntax.SyntaxTree
+
let diagnostics: [Diagnostic]
+
}
static let versionDependencies: [any FXVersioning.Type] = [ReadContents.self, Tokenise.self]
···
let tokens = try await ctx.request(Tokenise(blobId: blobId))
var parser = Parser(source: code, tokens: tokens)
PterodactylSyntax.Document.parse(&parser)
+
return ValueType(tree: parser.tree, diagnostics: parser.diagnostics)
}
}
}
-35
Sources/PterodactylBuild/Keys/Blob/ParseImports.swift
···
}
-
private struct ImportParser {
-
private var lexer: PterodactylSyntax.Lexer
-
public private(set) var imports: [String] = []
-
public init(input: String) {
-
self.lexer = PterodactylSyntax.Lexer(input: input)
-
}
-
-
public mutating func parseHeader() {
-
while true {
-
guard let token = nextSignificantToken() else { return }
-
switch token.kind {
-
case .keyword(.import): parseImportStatement()
-
default: return
-
}
-
}
-
}
-
-
/// Returns the next non-whitespace token.
-
private mutating func nextSignificantToken() -> Token? {
-
var token = lexer.nextToken()
-
while token?.kind.isTrivia == true {
-
token = lexer.nextToken()
-
}
-
-
guard let token else { return nil}
-
return Token(kind: token.kind, text: token.text)
-
}
-
-
/// Parses a single `import xyz` line.
-
private mutating func parseImportStatement() {
-
guard let next = nextSignificantToken() else { return }
-
guard next.kind == .identifier else { return }
-
imports.append(next.text)
-
}
-
}
···
}
+6
Sources/PterodactylBuild/Keys/Blob/README.md
···
Certain operations do not require knowledge of the entire source tree, only a specific blob inside the source tree. These include import analysis, tokenisation, line maps, parsing, etc.
···
+
<!--
+
SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers
+
+
SPDX-License-Identifier: MPL-2.0
+
-->
+
Certain operations do not require knowledge of the entire source tree, only a specific blob inside the source tree. These include import analysis, tokenisation, line maps, parsing, etc.
+2 -2
Sources/PterodactylBuild/Keys/SourceTree/GetUnitMap.swift
···
let sourceTreeId: LLBDataID
typealias ValueType = UnitMap
-
func computeValue(_ ctx: BuildContext<Self>) async throws -> UnitMap {
let sourceTree = try await LLBCASFileTree.load(id: sourceTreeId, in: ctx)
var units: [UnitName: UnitInfo] = [:]
try await sourceTree.traverse(root: .root, in: ctx) { path, blobID, directoryEntry in
-
let unitName = UnitName.fromPath(path)
units[unitName] = UnitInfo(path: path, blobId: blobID)
}
···
let sourceTreeId: LLBDataID
typealias ValueType = UnitMap
+
func computeValue(_ ctx: BuildContext<Self>) async throws -> UnitMap {
let sourceTree = try await LLBCASFileTree.load(id: sourceTreeId, in: ctx)
var units: [UnitName: UnitInfo] = [:]
try await sourceTree.traverse(root: .root, in: ctx) { path, blobID, directoryEntry in
+
let unitName = UnitName.fromPath(path)
units[unitName] = UnitInfo(path: path, blobId: blobID)
}
-53
Sources/PterodactylSyntax/Cursor.swift
···
-
// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers
-
//
-
// SPDX-License-Identifier: MPL-2.0
-
-
import Foundation
-
import LanguageServerProtocol
-
-
extension SyntaxTree {
-
public final class Cursor {
-
public let lineMap: LineMap
-
public let node: SyntaxTree.Child
-
public let utf16Offset: Int
-
-
public private(set) lazy var children: [Cursor] = {
-
var children: [Cursor] = []
-
var utf16Offset = utf16Offset
-
for childNode in node.children {
-
children.append(Self(lineMap: lineMap, node: childNode, utf16Offset: utf16Offset))
-
utf16Offset += childNode.utf16Length
-
}
-
-
return children
-
}()
-
-
public var utf16Range: Range<Int> {
-
utf16Offset..<utf16Offset + node.utf16Length
-
}
-
-
init(lineMap: LineMap, node: SyntaxTree.Child, utf16Offset: Int) {
-
self.lineMap = lineMap
-
self.node = node
-
self.utf16Offset = utf16Offset
-
}
-
}
-
}
-
-
extension SyntaxTree.Cursor {
-
public func firstChild<T>(mapping: (SyntaxTree.Cursor) -> T?) -> T? {
-
for child in children {
-
if let result = mapping(child) {
-
return result
-
} else {
-
continue
-
}
-
}
-
return nil
-
}
-
-
public func children<T>(mapping: (SyntaxTree.Cursor) -> T?) -> [T] {
-
children.compactMap(mapping)
-
}
-
}
-
···
+5 -5
Sources/PterodactylSyntax/Diagnostic.swift
···
//
// SPDX-License-Identifier: MPL-2.0
-
public struct Diagnostic: Equatable {
-
enum Severity: Equatable {
case error
case warning
case note
}
let message: String
let severity: Severity
-
/// Absolute UTF-16 code unit offsets from start of source
-
let absoluteRange: Range<Int>
init(message: String, severity: Severity, absoluteRange: Range<Int>) {
self.message = message
self.severity = severity
-
self.absoluteRange = absoluteRange
}
init(message: String, absoluteRange: Range<Int>) {
···
//
// SPDX-License-Identifier: MPL-2.0
+
public struct Diagnostic: Equatable, Codable, Sendable {
+
enum Severity: Equatable, Codable {
case error
case warning
case note
}
+
let message: String
let severity: Severity
+
let absoluteUtf16Range: Range<Int>
init(message: String, severity: Severity, absoluteRange: Range<Int>) {
self.message = message
self.severity = severity
+
self.absoluteUtf16Range = absoluteRange
}
init(message: String, absoluteRange: Range<Int>) {
+4 -5
Sources/PterodactylSyntax/FoldingRanges.swift
···
case reverse
}
-
extension Array {
-
fileprivate func apply(symmetry: ArraySymmetry) -> any Collection<Element> {
switch symmetry {
case .identity: self
case .reverse: reversed()
···
}
}
-
extension SyntaxTree.Cursor {
-
private func firstVisibleNode(under symmetry: ArraySymmetry) -> SyntaxTree.Cursor? {
switch node {
case .token(let token, _):
return token.kind.isVisible ? self : nil
case .tree:
for child in children.apply(symmetry: symmetry) {
if let visibleChild = child.firstVisibleNode(under: symmetry) { return visibleChild }
-
continue
}
return nil
···
case reverse
}
+
fileprivate extension Array {
+
func apply(symmetry: ArraySymmetry) -> any Collection<Element> {
switch symmetry {
case .identity: self
case .reverse: reversed()
···
}
}
+
extension SyntaxCursor {
+
private func firstVisibleNode(under symmetry: ArraySymmetry) -> SyntaxCursor? {
switch node {
case .token(let token, _):
return token.kind.isVisible ? self : nil
case .tree:
for child in children.apply(symmetry: symmetry) {
if let visibleChild = child.firstVisibleNode(under: symmetry) { return visibleChild }
}
return nil
+11 -5
Sources/PterodactylSyntax/Grammar.swift
···
import Foundation
public protocol Grammar: Sendable {
static var kinds: [SyntaxTreeKind] { get }
static func before(_ parser: inout Parser) -> Bool
static func inside(_ parser: inout Parser) -> ParseResult
}
public struct ParseResult {
public var kind: SyntaxTreeKind
public var metadata: SyntaxTreeMetadata? = nil
-
public init(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata? = nil) {
self.kind = kind
self.metadata = metadata
···
}
-
public extension Grammar {
-
static func tryParse(_ parser: inout Parser) -> Bool {
guard !parser.isEndOfFile && before(&parser) else { return false }
parse(&parser)
return true
}
-
-
static func parse(_ parser: inout Parser) {
let mark = parser.open()
let result = inside(&parser)
parser.close(mark: mark, kind: result.kind, metadata: result.metadata)
···
import Foundation
+
/// This is an abstraction of grammatical productions.
public protocol Grammar: Sendable {
+
/// The kinds of tree that the production produces.
static var kinds: [SyntaxTreeKind] { get }
+
+
/// Indicates whether the current parser state is consistent with the grammatical production starting here. When a given grammatical element is optional, this can be used to avoid backtracking. This is a *precondition* for parsing.
static func before(_ parser: inout Parser) -> Bool
+
+
/// Parse the grammatical production, assuming the precondition indicated by ``before(_:)``. This function should not be called outside this module (instead, use ``parse(_:)`` and ``tryParse(_:)``.
static func inside(_ parser: inout Parser) -> ParseResult
}
public struct ParseResult {
public var kind: SyntaxTreeKind
public var metadata: SyntaxTreeMetadata? = nil
+
public init(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata? = nil) {
self.kind = kind
self.metadata = metadata
···
}
+
extension Grammar {
+
public static func tryParse(_ parser: inout Parser) -> Bool {
guard !parser.isEndOfFile && before(&parser) else { return false }
parse(&parser)
return true
}
+
+
public static func parse(_ parser: inout Parser) {
let mark = parser.open()
let result = inside(&parser)
parser.close(mark: mark, kind: result.kind, metadata: result.metadata)
+2 -2
Sources/PterodactylSyntax/Grammar/Document/Theory/Declaration.swift
···
}
extension SyntaxView<Declaration> {
-
var lhs: SyntaxView<Lhs>? { matchingSubview() }
-
var rhs: SyntaxView<Rhs>? { matchingSubview() }
}
···
}
extension SyntaxView<Declaration> {
+
var lhs: SyntaxView<Declaration.Lhs>? { matchingSubview() }
+
var rhs: SyntaxView<Declaration.Rhs>? { matchingSubview() }
}
+13 -11
Sources/PterodactylSyntax/Grammar/Document/Theory/Declaration/Lhs.swift
···
import Foundation
-
enum Lhs: Grammar {
-
static let kind = SyntaxTreeKind(name: "declaration.lhs")
-
static let kinds = [kind]
-
-
static func before(_ parser: inout Parser) -> Bool {
-
parser.isAt(kind: .identifier)
-
}
-
-
static func inside(_ parser: inout Parser) -> ParseResult {
-
parser.expect(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .method))
-
return ParseResult(kind: Self.kind)
}
}
···
import Foundation
+
extension Declaration {
+
enum Lhs: Grammar {
+
static let kind = SyntaxTreeKind(name: "declaration.lhs")
+
static let kinds = [kind]
+
+
static func before(_ parser: inout Parser) -> Bool {
+
parser.isAt(kind: .identifier)
+
}
+
+
static func inside(_ parser: inout Parser) -> ParseResult {
+
parser.expect(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .method))
+
return ParseResult(kind: Self.kind)
+
}
}
}
+13 -11
Sources/PterodactylSyntax/Grammar/Document/Theory/Declaration/Rhs.swift
···
import Foundation
-
enum Rhs: Grammar {
-
static let kind = SyntaxTreeKind(name: "declaration.lhs")
-
static let kinds = [kind]
-
-
static func before(_ parser: inout Parser) -> Bool {
-
parser.isAt(kind: .identifier)
-
}
-
-
static func inside(_ parser: inout Parser) -> ParseResult {
-
parser.expect(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .method))
-
return ParseResult(kind: Self.kind)
}
}
···
import Foundation
+
extension Declaration {
+
enum Rhs: Grammar {
+
static let kind = SyntaxTreeKind(name: "declaration.lhs")
+
static let kinds = [kind]
+
+
static func before(_ parser: inout Parser) -> Bool {
+
parser.isAt(kind: .identifier)
+
}
+
+
static func inside(_ parser: inout Parser) -> ParseResult {
+
parser.expect(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .method))
+
return ParseResult(kind: Self.kind)
+
}
}
}
+42
Sources/PterodactylSyntax/ImportParser.swift
···
···
+
// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers
+
//
+
// SPDX-License-Identifier: MPL-2.0
+
+
import Foundation
+
+
public struct ImportParser {
+
private var lexer: PterodactylSyntax.Lexer
+
public private(set) var imports: [String] = []
+
+
public init(input: String) {
+
self.lexer = PterodactylSyntax.Lexer(input: input)
+
}
+
+
public mutating func parseHeader() {
+
while true {
+
guard let token = nextSignificantToken() else { return }
+
switch token.kind {
+
case .keyword(.import): parseImportStatement()
+
default: return
+
}
+
}
+
}
+
+
/// Returns the next non-whitespace token.
+
private mutating func nextSignificantToken() -> Token? {
+
var token = lexer.nextToken()
+
while token?.kind.isTrivia == true {
+
token = lexer.nextToken()
+
}
+
+
guard let token else { return nil }
+
return Token(kind: token.kind, text: token.text)
+
}
+
+
/// Parses a single `import xyz` line.
+
private mutating func parseImportStatement() {
+
guard let next = nextSignificantToken() else { return }
+
guard next.kind == .identifier else { return }
+
imports.append(next.text)
+
}
+
}
+1
Sources/PterodactylSyntax/Lexer.swift
···
import Foundation
public struct Lexer {
private let input: String
private var index: String.Index
···
import Foundation
+
/// This tokenises a string, without handling block layout at all. The tokens produced here should be fed into the ``BlockLayoutProcessor``.
public struct Lexer {
private let input: String
private var index: String.Index
+1 -1
Sources/PterodactylSyntax/SemanticToken.swift
···
}
}
-
extension SyntaxTree.Cursor {
var singleLineRanges: [SingleLineRange] {
var result: [SingleLineRange] = []
var location = lineMap.location(at: utf16Offset)
···
}
}
+
extension SyntaxCursor {
var singleLineRanges: [SingleLineRange] {
var result: [SingleLineRange] = []
var location = lineMap.location(at: utf16Offset)
+51
Sources/PterodactylSyntax/SyntaxCursor.swift
···
···
+
// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers
+
//
+
// SPDX-License-Identifier: MPL-2.0
+
+
import Foundation
+
import LanguageServerProtocol
+
+
/// This is a “red tree” in the sense of Roslyn. In essence it instruments syntax trees with non-relative location information.
+
public final class SyntaxCursor {
+
public let lineMap: LineMap
+
public let node: SyntaxTree.Child
+
public let utf16Offset: Int
+
+
public private(set) lazy var children: [SyntaxCursor] = {
+
var children: [SyntaxCursor] = []
+
var utf16Offset = utf16Offset
+
for childNode in node.children {
+
children.append(Self(lineMap: lineMap, node: childNode, utf16Offset: utf16Offset))
+
utf16Offset += childNode.utf16Length
+
}
+
+
return children
+
}()
+
+
public var utf16Range: Range<Int> {
+
utf16Offset..<utf16Offset + node.utf16Length
+
}
+
+
init(lineMap: LineMap, node: SyntaxTree.Child, utf16Offset: Int) {
+
self.lineMap = lineMap
+
self.node = node
+
self.utf16Offset = utf16Offset
+
}
+
}
+
+
extension SyntaxCursor {
+
public func firstChild<T>(mapping: (SyntaxCursor) -> T?) -> T? {
+
for child in children {
+
if let result = mapping(child) {
+
return result
+
} else {
+
continue
+
}
+
}
+
return nil
+
}
+
+
public func children<T>(mapping: (SyntaxCursor) -> T?) -> [T] {
+
children.compactMap(mapping)
+
}
+
}
+1
Sources/PterodactylSyntax/SyntaxTree.swift
···
import Foundation
public struct SyntaxTree: Codable, Sendable {
public let kind: SyntaxTreeKind
public let metadata: SyntaxTreeMetadata?
···
import Foundation
+
/// This is a “green tree” in the sense of Roslyn.
public struct SyntaxTree: Codable, Sendable {
public let kind: SyntaxTreeKind
public let metadata: SyntaxTreeMetadata?
+3 -2
Sources/PterodactylSyntax/SyntaxView.swift
···
import Foundation
struct SyntaxView<G: Grammar> {
-
let cursor: SyntaxTree.Cursor
-
init?(_ cursor: SyntaxTree.Cursor) {
guard let kind = cursor.node.tree?.kind, G.kinds.contains(kind) else { return nil }
self.cursor = cursor
}
···
import Foundation
+
/// An abstract syntax view around a ``SyntaxCursor``. This is to be populated by extensions targetting specific `G`.
struct SyntaxView<G: Grammar> {
+
let cursor: SyntaxCursor
+
init?(_ cursor: SyntaxCursor) {
guard let kind = cursor.node.tree?.kind, G.kinds.contains(kind) else { return nil }
self.cursor = cursor
}