Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7a573c4
yeast: Order AST dump fields by schema-declared order
tausbn Jul 16, 2026
8206f6f
swift-syntax-rs: Fold local and stdlib operators
tausbn Jul 16, 2026
2e74c1d
yeast: Desugar an externally-built AST, and validate by field name
tausbn Jul 16, 2026
bbf52ce
tree-sitter-extractor: Split direct and desugaring extractors
tausbn Jul 17, 2026
79954ce
unified: Add the swift-syntax parser and unresolved operator sequence…
tausbn Jul 17, 2026
ad2ce93
unified: Port top-level, literal, and name rules to swift-syntax
tausbn Jul 16, 2026
8328fba
unified: Port operator rules to swift-syntax
tausbn Jul 17, 2026
437e482
unified: Port variable-binding rules to swift-syntax
tausbn Jul 17, 2026
191fc54
unified: Port type-expression rules to swift-syntax
tausbn Jul 17, 2026
52cdf59
unified: Port function, call, and member-access rules to swift-syntax
tausbn Jul 17, 2026
6ad1fac
unified: Port closure rules to swift-syntax
tausbn Jul 17, 2026
2de1549
unified: Port control-flow and pattern rules to swift-syntax
tausbn Jul 20, 2026
2fcbc9b
unified: Port loop rules to swift-syntax
tausbn Jul 20, 2026
4c764b5
unified: Port collection rules to swift-syntax
tausbn Jul 20, 2026
6275977
unified: Port optional and error-handling rules to swift-syntax
tausbn Jul 20, 2026
37654b4
unified: Port import rules to swift-syntax
tausbn Jul 20, 2026
8e6651a
unified: Port type-container declarations to swift-syntax
tausbn Jul 20, 2026
ec0c49a
unified: Port property accessor rules to swift-syntax
tausbn Jul 22, 2026
90ea1b5
unified: Port enum-case rules to swift-syntax
tausbn Jul 23, 2026
c4fbd74
unified: Port constructor and related declaration rules to swift-syntax
tausbn Jul 23, 2026
5c5fd5e
unified: Switch the Swift front-end to swift-syntax
tausbn Jul 23, 2026
20a2537
unified: Regenerate the raw-AST corpus section for swift-syntax
tausbn Jul 23, 2026
046c88a
unified: Regenerate the enhanced getter/setter property corpus case
tausbn Jul 23, 2026
c50bcba
swift-syntax-rs: Degrade gracefully without a Swift toolchain
tausbn Jul 23, 2026
17cbb4e
unified: Harden the external Swift parser integration
tausbn Jul 24, 2026
7721ce2
unified: Add swift_node_types.yml to the extractor's compile_data
tausbn Jul 24, 2026
e3a0822
unified: Package the swift-syntax parser in the extractor pack
tausbn Jul 24, 2026
ba26e1d
unified: Add corpus tests for nested types
tausbn Jul 27, 2026
a3d9492
Merge branch 'main' into tausbn/swift-syntax-rs-sequenced
tausbn Jul 28, 2026
a2ff35a
unified: Fix Bazel formatting errors
tausbn Jul 28, 2026
ceffe40
unified: Remove references to Swift input schema generation
tausbn Jul 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 57 additions & 63 deletions unified/extractor/src/languages/swift/swift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ fn member_chain(
)
}

/// Compound-assignment operator spellings (`+=`, `<<=`, ...). Used to tell a
/// compound assignment from an ordinary binary application, both of which
/// arrive as a `binaryOperator`-based `infixOperatorExpr`.
const COMPOUND_ASSIGN_OPS: &[&str] = &[
"+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=", "^=", "&+=", "&-=", "&*=",
Comment thread
jketema marked this conversation as resolved.
];

fn translation_rules() -> Vec<Rule<SwiftContext>> {
vec![
// ---- Top-level ----
Expand Down Expand Up @@ -159,55 +166,56 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
// `name_expr` too.
rule!((discardAssignmentExpr wildcard: @@w) => (name_expr identifier: (identifier #{w}))),
// ---- Operators ----
// All binary operators share the lhs/op/rhs shape.
rule!((additive_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((multiplicative_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((comparison_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((equality_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((conjunction_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((disjunction_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((infix_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
// Range expression `a..<b` / `a...b`
rule!((range_expression start: @l op: @op end: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
// Open-ended ranges `a...` / `...b`
rule!((open_end_range_expression start: @l) => (unary_expr operator: (postfix_operator "...") operand: {l})),
rule!((open_start_range_expression end: @r) => (unary_expr operator: (prefix_operator "...") operand: {r})),
// Custom operator declaration: `[prefix|infix|postfix] operator OP [: PrecedenceGroup]`.
// The fixity keyword is an anonymous child of `operator_declaration`, so we
// dispatch on it with one rule per keyword.
rule!(
(operator_declaration "prefix" (referenceable_operator _ @op) (simple_identifier)? @prec)
=>
(operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "prefix") precedence: {prec})
),
rule!(
(operator_declaration "postfix" (referenceable_operator _ @op) (simple_identifier)? @prec)
=>
(operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "postfix") precedence: {prec})
),
rule!(
(operator_declaration "infix" (referenceable_operator _ @op) (simple_identifier)? @prec)
=>
(operator_syntax_declaration
name: (identifier #{op})
fixity: (fixity "infix")
precedence: {prec})
),
rule!((bitwise_operation lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})),
rule!((nil_coalescing_expression value: @l if_nil: @r) => (binary_expr left: {l} operator: (infix_operator "??") right: {r})),
// Leading-dot member shorthand (e.g. `.some`, `.foo`) means member access
// on a contextually inferred type.
rule!((prefix_expression operation: "." target: @member) => (member_access_expr base: (inferred_type_expr) member: (identifier #{member}))),
// Prefix unary operators
rule!((prefix_expression operation: @op target: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})),
// Postfix unary operators
rule!((postfix_expression operation: @op target: @operand) => (unary_expr operator: (postfix_operator #{op}) operand: {operand})),
// TODO: Parenthesised single-value tuple is a grouping expression and should pass through.
// Multi-value tuples become tuple_expr.
rule!((tuple_expression value: _* @v) => (tuple_expr element: {v})),
// Blocks contain statement* directly.
rule!((block statement: _+ @stmts) => (block stmt: {stmts})),
rule!((block) => (block)),
// The parser front-end folds operator chains into nested
// `infixOperatorExpr`s by precedence (see swift-syntax-rs), so
// `1 + 2 * 3` arrives here already structured.
//
// A `binaryOperatorExpr` wraps the operator token; unwrap it to the
// operator leaf. Used by `infixOperatorExpr` (folded) and `sequenceExpr`
// (unresolved).
rule!((binaryOperatorExpr operator: @op) => (infix_operator #{op})),
// Compound assignment (`x += y`) vs. an ordinary binary application
// (`a + b`): both are `binaryOperator`-based `infixOperatorExpr`s,
// distinguishable only by the operator's spelling. The query engine
// can't match on token text, so a small Rust block reads the spelling
// and routes to `compound_assign_expr` or `binary_expr`. The operator
// is captured raw (`@@op`) to read its spelling.
rule!(
(infixOperatorExpr leftOperand: @l operator: (binaryOperatorExpr) @@op rightOperand: @r)
=>
expr {
if COMPOUND_ASSIGN_OPS.contains(&ctx.source_text(op).as_str()) {
tree!((compound_assign_expr target: {l} operator: (infix_operator #{op}) value: {r}))
} else {
tree!((binary_expr left: {l} operator: (infix_operator #{op}) right: {r}))
}
}
),
// Plain assignment (`x = y`). In a folded chain the `=` is an
// `assignmentExpr` node (distinct from other operators), matched by kind.
rule!(
(infixOperatorExpr leftOperand: @l operator: (assignmentExpr) rightOperand: @r)
=>
(assign_expr target: {l} value: {r})
),
// Escape hatch: an operator chain the front-end could not resolve
// (because it uses an operator of unknown precedence, e.g. imported from
// another module) stays a flat `sequenceExpr`. Preserve it as an
// `unresolved_operator_sequence` whose elements alternate operands and
// infix operators, rather than guessing a structure.
rule!((sequenceExpr elements: _* @els) => (unresolved_operator_sequence element: {els})),
Comment thread
tausbn marked this conversation as resolved.
// Prefix unary operators (`!a`, `-x`).
rule!((prefixOperatorExpr operator: @op expression: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})),
// A `tupleExpr` is a tuple literal (`(a, b)`) or a parenthesised
// expression (`(x)`). For now it is kept as an opaque `tuple_expr` leaf
// (its source text); its elements are not descended into.
//
// TODO: a parenthesised single-element `tupleExpr` is really a grouping
// expression and should be elided (unwrapped to its inner expression)
// rather than modelled as a tuple.
rule!((tupleExpr) => (tuple_expr)),
// A code block contains its statements directly.
rule!((codeBlock statements: _* @stmts) => (block stmt: {stmts})),
// ---- Variables ----
// property_binding rules — these produce variable_declaration and/or accessor_declaration
// nodes for individual declarators. The outer property_declaration rule splices these out
Expand Down Expand Up @@ -441,22 +449,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
result
}
),
// Plain assignment: `x = expr`
rule!(
(assignment operator: "=" target: (directly_assignable_expression expr: @target) result: @value)
=>
(assign_expr target: {target} value: {value})
),
// Compound assignment: `x += expr` etc.
rule!(
(assignment operator: @op target: (directly_assignable_expression expr: @target) result: @value)
=>
(compound_assign_expr target: {target} operator: (infix_operator #{op}) value: {value})
),
// Unwrap `type` wrapper node
rule!((type name: @inner) => type_expr { inner }),
// `directly_assignable_expression` is just a wrapper; unwrap it
rule!((directly_assignable_expression expr: @inner) => expr { inner }),
// Pattern with bound_identifier → name_pattern.
rule!(
(pattern bound_identifier: @name)
Expand Down