Constituency Trees #
The constituency tree and LF interface format of the Syntax layer:
one tree type, parameterized by node labels (C) and terminal content
(W), shared by the frameworks whose structures are constituency
trees — not by all of syntax. The library has two structural hubs:
Syntax.Tree(this file) — for constituency: H&K composition reads it (LF), Katzir structural operations transform it (PF), Minimalist derivations project down to it at Spell-Out.Core.Order.TreeOrder+ the B&P command library — the genuinely framework-agnostic layer, where non-constituency frameworks connect via their own node types (HPSG signs, DG word-indices, Minimalist syntactic objects), each with its own dominance order.
Frameworks whose structures are not trees do not and cannot
instantiate this type: CCG derivations are proof trees (nodes are rule
applications); HPSG signs are reentrant feature structures (structure
sharing has no tree representation); dependency structures are
head-indexed graphs; multidominance objects are nonplanar (which is why
Syntax/Minimalist/ keeps its own SyntacticObject and only projects
to Tree at the interface).
Syntax.Tree C W #
N-ary branching with categories on every node. Read by both interfaces:
- Compositional interpretation (LF):
interp/evalTreeinSemantics/Composition/Tree.lean— type-driven, ignores categories - Structural operations (PF): [Kat07]
StructOp(substitution, deletion, contraction) inSemantics/Alternatives/Structural.lean— category-aware
The generic core is terminal/node with the pluggable category
parameter C (UD tags, feature bundles, Unit for unlabeled, ...).
The trace and bind constructors encode the trace-theoretic / QR
analysis of movement specifically ([HK98] Ch. 5): indexed
traces plus λ-binders. Rival representations of movement are not
expressible on this type — copy theory needs a side-car chain relation
over TreePaths, and multidominance needs the nonplanar
SyntacticObject (Syntax/Minimalist/Multidominance.lean). Frameworks
without movement simply never construct these cases. Binder–trace
well-formedness (each bind n binding a matching trace n) is a
predicate to be imposed, not a type guarantee; bind's category label
is recorded for uniformity but is not consulted by interp or
StructOp.
Instantiations #
Tree Unit String— category-free, for H&K composition. Use convenience constructors.leaf,.bin,.un,.tr,.binder.Tree Cat String— UD-grounded categories (Syntax/Tree/Cat.lean), for Katzir structural alternatives.Tree Unit LIToken— bare phrase structure projected from Minimalist syntax at Spell-Out viaFreeMagma.toTree(categories inside the token, not on nodes).
Positions and dominance #
Tree is an instance of Core.Order.Branching (the rose-tree
interface), so all position machinery is inherited rather than
re-stipulated: Gorn addresses (Core.Order.TreePath), the dominance
order with mathlib's rooted-tree stack (root ⊥, parent Order.pred,
least common ancestor ⊓), and the forgetful map
Core.Order.Branching.toTreeOrder into the B&P command-relation
library (Linglib.Core.Order.Command, [BP90]).
The tree type #
Constituency tree, parameterized by node label type C and terminal
word type W.
terminal c w— leaf carrying categorycand wordwnode c cs— internal node with categorycand childrencstrace n c— movement trace with indexnand categorycbind n c body— λ-abstraction with indexn, categoryc, scopebody
The node constructor takes a List of children, subsuming both
binary branching (for Heim & Kratzer composition) and n-ary structure
(for Katzir's deletion operation). Binary nodes are node c [l, r].
trace and bind support Quantifier Raising and variable binding.
Frameworks without movement (CCG, HPSG) simply never construct these.
For category-free trees (C = Unit), use the convenience constructors
leaf, bin, un, tr, binder which hide the Unit parameter.
- terminal {C : Type u_1} {W : Type u_2} : C → W → Tree C W
- node {C : Type u_1} {W : Type u_2} : C → List (Tree C W) → Tree C W
- trace {C : Type u_1} {W : Type u_2} : ℕ → C → Tree C W
- bind {C : Type u_1} {W : Type u_2} : ℕ → C → Tree C W → Tree C W
Instances For
Equations
- Syntax.instReprTree = { reprPrec := Syntax.instReprTree.repr }
Equations
- Syntax.Tree.leaf w = Syntax.Tree.terminal () w
Instances For
Equations
- Syntax.Tree.binder n body = Syntax.Tree.bind n () body
Instances For
Basic accessors #
Category label of the root node. Total: every constructor carries
a category (including bind, where it labels the result of PA).
Equations
- (Syntax.Tree.terminal a a_1).cat = a
- (Syntax.Tree.node a a_1).cat = a
- (Syntax.Tree.trace a a_1).cat = a_1
- (Syntax.Tree.bind a a_1 a_2).cat = a_1
Instances For
Decidable equality #
DecidableEq is the single source of truth for tree equality; BEq and
LawfulBEq (Tree C W) come for free (and coherently) from the global
instBEqOfDecidableEq. A hand-rolled beq/BEq instance used to shadow
this — it left LawfulBEq (Tree C W) unsynthesizable and was a second,
unproven-coherent notion of tree equality, so it was removed.
Decidable equality on Tree C W, mutually recursive with the
list-of-trees case. Required because deriving DecidableEq does not
handle the nested List (Tree C W) in node.
Equations
- One or more equations did not get rendered due to their size.
- (Syntax.Tree.terminal c₁ w₁).decEq (Syntax.Tree.terminal c₂ w₂) = if hc : c₁ = c₂ then if hw : w₁ = w₂ then isTrue ⋯ else isFalse ⋯ else isFalse ⋯
- (Syntax.Tree.trace n₁ c₁).decEq (Syntax.Tree.trace n₂ c₂) = if hn : n₁ = n₂ then if hc : c₁ = c₂ then isTrue ⋯ else isFalse ⋯ else isFalse ⋯
- (Syntax.Tree.terminal a a_1).decEq (Syntax.Tree.node a_2 a_3) = isFalse ⋯
- (Syntax.Tree.terminal a a_1).decEq (Syntax.Tree.trace a_2 a_3) = isFalse ⋯
- (Syntax.Tree.terminal a a_1).decEq (Syntax.Tree.bind a_2 a_3 a_4) = isFalse ⋯
- (Syntax.Tree.node a a_1).decEq (Syntax.Tree.terminal a_2 a_3) = isFalse ⋯
- (Syntax.Tree.node a a_1).decEq (Syntax.Tree.trace a_2 a_3) = isFalse ⋯
- (Syntax.Tree.node a a_1).decEq (Syntax.Tree.bind a_2 a_3 a_4) = isFalse ⋯
- (Syntax.Tree.trace a a_1).decEq (Syntax.Tree.terminal a_2 a_3) = isFalse ⋯
- (Syntax.Tree.trace a a_1).decEq (Syntax.Tree.node a_2 a_3) = isFalse ⋯
- (Syntax.Tree.trace a a_1).decEq (Syntax.Tree.bind a_2 a_3 a_4) = isFalse ⋯
- (Syntax.Tree.bind a a_1 a_2).decEq (Syntax.Tree.terminal a_3 a_4) = isFalse ⋯
- (Syntax.Tree.bind a a_1 a_2).decEq (Syntax.Tree.node a_3 a_4) = isFalse ⋯
- (Syntax.Tree.bind a a_1 a_2).decEq (Syntax.Tree.trace a_3 a_4) = isFalse ⋯
Instances For
Helper: decidable equality for list of trees.
Equations
- Syntax.Tree.decEqList [] [] = isTrue ⋯
- Syntax.Tree.decEqList [] (head :: tail) = isFalse ⋯
- Syntax.Tree.decEqList (head :: tail) [] = isFalse ⋯
- Syntax.Tree.decEqList (a :: as) (b :: bs) = match a.decEq b, Syntax.Tree.decEqList as bs with | isTrue ha, isTrue has => isTrue ⋯ | isFalse ha, x => isFalse ⋯ | x, isFalse has => isFalse ⋯
Instances For
Equations
Size #
Number of nodes in the tree.
Equations
- (Syntax.Tree.terminal a a_1).size = 1
- (Syntax.Tree.node a a_1).size = 1 + Syntax.Tree.size.sizeList a_1
- (Syntax.Tree.trace a a_1).size = 1
- (Syntax.Tree.bind a a_1 a_2).size = 1 + a_2.size
Instances For
Equations
- Syntax.Tree.size.sizeList [] = 0
- Syntax.Tree.size.sizeList (t :: ts) = t.size + Syntax.Tree.size.sizeList ts
Instances For
Number of word-bearing terminals (leaves) in the tree. Traces and binders contribute 0; internal nodes recurse.
Equations
- (Syntax.Tree.terminal a a_1).leafCount = 1
- (Syntax.Tree.node a a_1).leafCount = Syntax.Tree.leafCount.leafCountList a_1
- (Syntax.Tree.trace a a_1).leafCount = 0
- (Syntax.Tree.bind a a_1 a_2).leafCount = a_2.leafCount
Instances For
Equations
Instances For
Induction principle #
Membership-based recursor/induction principle for Tree. The default
induction tactic refuses nested inductives, and the raw two-motive
Tree.rec forces a parallel List motive; this principle hands the
node case the hypothesis proofs actually want — ∀ t ∈ cs, motive t —
so structural inductions read directly. With @[elab_as_elim], used as
induction t using Tree.recAux with | terminal … | node c cs ih | trace … | bind ….
Equations
- Syntax.Tree.recAux terminal node trace bind (Syntax.Tree.terminal a a_1) = terminal a a_1
- Syntax.Tree.recAux terminal node trace bind (Syntax.Tree.node a a_1) = node a a_1 fun (t : Syntax.Tree C W) (x : t ∈ a_1) => Syntax.Tree.recAux terminal node trace bind t
- Syntax.Tree.recAux terminal node trace bind (Syntax.Tree.trace a a_1) = trace a a_1
- Syntax.Tree.recAux terminal node trace bind (Syntax.Tree.bind a a_1 a_2) = bind a a_1 a_2 (Syntax.Tree.recAux terminal node trace bind a_2)
Instances For
Subtrees #
All subtrees including self (pre-order traversal).
Equations
- (Syntax.Tree.terminal a a_1).subtrees = [Syntax.Tree.terminal a a_1]
- (Syntax.Tree.node a cs).subtrees = Syntax.Tree.node a cs :: Syntax.Tree.subtrees.subtreesList cs
- (Syntax.Tree.trace a a_1).subtrees = [Syntax.Tree.trace a a_1]
- (Syntax.Tree.bind a a_1 body).subtrees = Syntax.Tree.bind a a_1 body :: body.subtrees
Instances For
Equations
- Syntax.Tree.subtrees.subtreesList [] = []
- Syntax.Tree.subtrees.subtreesList (t :: ts) = t.subtrees ++ Syntax.Tree.subtrees.subtreesList ts
Instances For
Category queries #
ContainsCat target t holds when category target appears on some
subtree of t. A Prop predicate (with a kernel-reducible Decidable
instance from List membership, so decide closes concrete goals) over
the file's own structurally-recursive subtrees — not a Bool function.
Equations
- Syntax.Tree.ContainsCat target t = (target ∈ List.map Syntax.Tree.cat t.subtrees)
Instances For
Equations
- Syntax.Tree.instDecidableContainsCat target t = Syntax.Tree.instDecidableContainsCat._aux_1 target t
ContainsCat at a node: it is the node's own category, or appears in
some child.
ContainsCat at a binder: the binder's category, or inside the body.
Leaf substitution #
Substitute all terminals of category c carrying word target
with replacement. This is the most common structural operation:
replacing one scalar item with another of the same category.
Equations
- Syntax.Tree.leafSubst target replacement c (Syntax.Tree.terminal a a_1) = if (c == a && a_1 == target) = true then Syntax.Tree.terminal a replacement else Syntax.Tree.terminal a a_1
- Syntax.Tree.leafSubst target replacement c (Syntax.Tree.node a a_1) = Syntax.Tree.node a (Syntax.Tree.leafSubst.leafSubstList target replacement c a_1)
- Syntax.Tree.leafSubst target replacement c (Syntax.Tree.trace a a_1) = Syntax.Tree.trace a a_1
- Syntax.Tree.leafSubst target replacement c (Syntax.Tree.bind a a_1 a_2) = Syntax.Tree.bind a a_1 (Syntax.Tree.leafSubst target replacement c a_2)
Instances For
Equations
- Syntax.Tree.leafSubst.leafSubstList target replacement c [] = []
- Syntax.Tree.leafSubst.leafSubstList target replacement c (t :: ts) = Syntax.Tree.leafSubst target replacement c t :: Syntax.Tree.leafSubst.leafSubstList target replacement c ts
Instances For
Branching instance: positions, dominance, command relations #
The path machinery (Gorn addresses, Core.Order.TreePath, the
dominance order, the B&P command-relation bridge) is generic over
Core.Order.Branching; Tree participates through the instance
below. Branching.subtreeAt at this instance behaves as before: for
node c cs the next index selects cs[i]?; for bind only index 0
is valid (binders have a single body); terminals and traces have no
children. Positions inherit mathlib's rooted-tree order stack from
TreePath: root ⊥, parent Order.pred, least common ancestor ⊓.
Equations
- One or more equations did not get rendered due to their size.
Children strictly decrease the sizeOf measure, unlocking the
generic recursion API (Branching.size, subtrees, yield,
inductionOn). noncomputable because the measure field stores
sizeOf, whose nested-List IR the LCNF boxing pass cannot compile;
the measure is only a termination witness, and yield/size reduce
symbolically via their _def lemmas.
Terminal content: the word at a terminal; traces, binders, and
internal nodes are contentless, so Branching.yield computes the
frontier string.
Equations
- Syntax.instHasContentTree = { content? := fun (x : Syntax.Tree C W) => match x with | Syntax.Tree.terminal a w => some w | x => none }
Branching.yield-instance simp lemmas #
Make the generic Branching.yield reduce by simp/decide at
concrete Tree constructors — the prerequisite for consumers (Studies
files) to replace bespoke yield computations with the generic API.
FreeMagma → Tree forgetful map #
Forgetful map from a free magma to a binary Syntax.Tree Unit α.
The image lives in a strict subset of Tree Unit α: only .terminal ()
(from .of) and the binary .node () [_, _] (from .mul) are produced;
the n-ary .node, .trace, and .bind constructors are never used.
By composition with Core.Order.Branching.toTreeOrder, every
FreeMagma α inherits a Core.Order.TreeOrder Core.Order.TreePath,
making B&P's framework-agnostic command-relation library
(Linglib.Core.Order.Command) directly applicable.
Universe-polymorphic in α, matching Syntax.Tree.
Equations
- (FreeMagma.of a).toTree = Syntax.Tree.terminal () a
- (l.mul r).toTree = Syntax.Tree.node () [l.toTree, r.toTree]