unicode-logic-kit 0.31.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unicode_logic_kit/__init__.py +385 -0
- unicode_logic_kit/__main__.py +520 -0
- unicode_logic_kit/_deadline.py +219 -0
- unicode_logic_kit/ace/__init__.py +126 -0
- unicode_logic_kit/ace/_align.py +135 -0
- unicode_logic_kit/ace/chem_lexicon.py +128 -0
- unicode_logic_kit/ace/drs_reader.py +570 -0
- unicode_logic_kit/ace/mapping.py +666 -0
- unicode_logic_kit/ace/reverse_modal.py +138 -0
- unicode_logic_kit/ace/runner.py +551 -0
- unicode_logic_kit/ace/translate.py +452 -0
- unicode_logic_kit/ace/verbalize.py +1070 -0
- unicode_logic_kit/api.py +1284 -0
- unicode_logic_kit/atp/__init__.py +177 -0
- unicode_logic_kit/atp/_ascii_names.py +113 -0
- unicode_logic_kit/atp/_html.py +72 -0
- unicode_logic_kit/atp/_substructural_input.py +228 -0
- unicode_logic_kit/atp/_tff_problem.py +715 -0
- unicode_logic_kit/atp/_tptp_problem.py +1111 -0
- unicode_logic_kit/atp/_writer_support.py +289 -0
- unicode_logic_kit/atp/clingo_backend.py +1180 -0
- unicode_logic_kit/atp/cvc5_backend.py +1385 -0
- unicode_logic_kit/atp/eprover_backend.py +732 -0
- unicode_logic_kit/atp/finite_domain.py +1055 -0
- unicode_logic_kit/atp/fitch.py +1547 -0
- unicode_logic_kit/atp/fitch_search.py +551 -0
- unicode_logic_kit/atp/hets_backend.py +339 -0
- unicode_logic_kit/atp/hybrid_down.py +120 -0
- unicode_logic_kit/atp/incremental.py +250 -0
- unicode_logic_kit/atp/kripke_enum.py +741 -0
- unicode_logic_kit/atp/lambek.py +436 -0
- unicode_logic_kit/atp/leo3_backend.py +332 -0
- unicode_logic_kit/atp/linear.py +738 -0
- unicode_logic_kit/atp/lj.py +705 -0
- unicode_logic_kit/atp/logic_backends.py +566 -0
- unicode_logic_kit/atp/ltl_tableau.py +1084 -0
- unicode_logic_kit/atp/minizinc_backend.py +1402 -0
- unicode_logic_kit/atp/modal_tableau.py +1382 -0
- unicode_logic_kit/atp/nanocop_backend.py +410 -0
- unicode_logic_kit/atp/portfolio.py +489 -0
- unicode_logic_kit/atp/protocol.py +1803 -0
- unicode_logic_kit/atp/prover9_entailment.py +1153 -0
- unicode_logic_kit/atp/resolution.py +1376 -0
- unicode_logic_kit/atp/resolution_check.py +1114 -0
- unicode_logic_kit/atp/sequent.py +1050 -0
- unicode_logic_kit/atp/tableau.py +921 -0
- unicode_logic_kit/atp/tableau_check.py +543 -0
- unicode_logic_kit/atp/tptp_ncl.py +811 -0
- unicode_logic_kit/atp/tptp_tff.py +1546 -0
- unicode_logic_kit/atp/tstp.py +1333 -0
- unicode_logic_kit/atp/tstp_check.py +1096 -0
- unicode_logic_kit/atp/twee_backend.py +236 -0
- unicode_logic_kit/atp/twee_check.py +711 -0
- unicode_logic_kit/atp/twee_entailment.py +953 -0
- unicode_logic_kit/atp/vampire_entailment.py +540 -0
- unicode_logic_kit/atp/z3_arith.py +470 -0
- unicode_logic_kit/atp/z3_equivalence.py +36 -0
- unicode_logic_kit/atp/z3_fuzzy.py +362 -0
- unicode_logic_kit/atp/z3_input.py +500 -0
- unicode_logic_kit/atp/z3_models.py +208 -0
- unicode_logic_kit/chem/__init__.py +88 -0
- unicode_logic_kit/chem/_naming.py +284 -0
- unicode_logic_kit/chem/cache.py +185 -0
- unicode_logic_kit/chem/interop.py +244 -0
- unicode_logic_kit/chem/mol.py +525 -0
- unicode_logic_kit/chem/signature.py +112 -0
- unicode_logic_kit/comorphism.py +497 -0
- unicode_logic_kit/dl/__init__.py +384 -0
- unicode_logic_kit/dl/classification.py +227 -0
- unicode_logic_kit/dl/concepts.py +632 -0
- unicode_logic_kit/dl/datatypes.py +818 -0
- unicode_logic_kit/dl/owl_functional.py +2433 -0
- unicode_logic_kit/dl/owl_manchester.py +1637 -0
- unicode_logic_kit/dl/owl_reasoner.py +790 -0
- unicode_logic_kit/dl/parser.py +391 -0
- unicode_logic_kit/dl/tableau.py +4048 -0
- unicode_logic_kit/dl/translate.py +2704 -0
- unicode_logic_kit/drt/__init__.py +94 -0
- unicode_logic_kit/drt/export.py +179 -0
- unicode_logic_kit/drt/nodes.py +506 -0
- unicode_logic_kit/drt/parser.py +965 -0
- unicode_logic_kit/drt/resolve.py +195 -0
- unicode_logic_kit/drt/reverse.py +175 -0
- unicode_logic_kit/eval/__init__.py +106 -0
- unicode_logic_kit/eval/batch.py +382 -0
- unicode_logic_kit/eval/canonical.py +663 -0
- unicode_logic_kit/eval/chem_batch.py +606 -0
- unicode_logic_kit/eval/converses.py +200 -0
- unicode_logic_kit/eval/datasets/__init__.py +136 -0
- unicode_logic_kit/eval/datasets/_base.py +263 -0
- unicode_logic_kit/eval/datasets/_proofwriter_proof.py +422 -0
- unicode_logic_kit/eval/datasets/c3po.py +678 -0
- unicode_logic_kit/eval/datasets/folio.py +158 -0
- unicode_logic_kit/eval/datasets/fracas.py +418 -0
- unicode_logic_kit/eval/datasets/groves.py +191 -0
- unicode_logic_kit/eval/datasets/logicbench.py +467 -0
- unicode_logic_kit/eval/datasets/logicnli.py +303 -0
- unicode_logic_kit/eval/datasets/malls.py +133 -0
- unicode_logic_kit/eval/datasets/pfolio.py +594 -0
- unicode_logic_kit/eval/datasets/pmb.py +242 -0
- unicode_logic_kit/eval/datasets/prontoqa.py +611 -0
- unicode_logic_kit/eval/datasets/proofwriter.py +1431 -0
- unicode_logic_kit/eval/datasets/proverqa.py +674 -0
- unicode_logic_kit/eval/datasets/willow.py +478 -0
- unicode_logic_kit/eval/equivalence.py +466 -0
- unicode_logic_kit/eval/exercise_gen.py +533 -0
- unicode_logic_kit/eval/explain.py +791 -0
- unicode_logic_kit/eval/generality.py +750 -0
- unicode_logic_kit/eval/metric_hf.py +458 -0
- unicode_logic_kit/eval/predicate_match.py +343 -0
- unicode_logic_kit/eval/theory_check.py +1170 -0
- unicode_logic_kit/eval/validate.py +306 -0
- unicode_logic_kit/fol/__init__.py +177 -0
- unicode_logic_kit/fol/_atom_keys.py +510 -0
- unicode_logic_kit/fol/_fol_nodes.py +3586 -0
- unicode_logic_kit/fol/_free_parameters.py +105 -0
- unicode_logic_kit/fol/_ho_nodes.py +448 -0
- unicode_logic_kit/fol/_hybrid_nodes.py +308 -0
- unicode_logic_kit/fol/_identifiers.py +1091 -0
- unicode_logic_kit/fol/_lambek_nodes.py +112 -0
- unicode_logic_kit/fol/_linear_nodes.py +352 -0
- unicode_logic_kit/fol/_modal_nodes.py +1467 -0
- unicode_logic_kit/fol/_msfl_nodes.py +2196 -0
- unicode_logic_kit/fol/_numeral_symbols.py +231 -0
- unicode_logic_kit/fol/_so_nodes.py +200 -0
- unicode_logic_kit/fol/_symbol_names.py +81 -0
- unicode_logic_kit/fol/_team_nodes.py +181 -0
- unicode_logic_kit/fol/_tptp_symbols.py +551 -0
- unicode_logic_kit/fol/_truth_constants.py +117 -0
- unicode_logic_kit/fol/casl_export.py +1135 -0
- unicode_logic_kit/fol/casl_import.py +929 -0
- unicode_logic_kit/fol/derivation.py +367 -0
- unicode_logic_kit/fol/dialect_detect.py +70 -0
- unicode_logic_kit/fol/dialect_repair.py +537 -0
- unicode_logic_kit/fol/frames.py +637 -0
- unicode_logic_kit/fol/grammars/terminals.lark +31 -0
- unicode_logic_kit/fol/lambda_tools.py +297 -0
- unicode_logic_kit/fol/latex_input.py +429 -0
- unicode_logic_kit/fol/modal_translation.py +944 -0
- unicode_logic_kit/fol/msflparser.py +1033 -0
- unicode_logic_kit/fol/naming.py +422 -0
- unicode_logic_kit/fol/nodes.py +241 -0
- unicode_logic_kit/fol/normalforms.py +492 -0
- unicode_logic_kit/fol/pal.py +287 -0
- unicode_logic_kit/fol/prolog_export.py +566 -0
- unicode_logic_kit/fol/prolog_input.py +505 -0
- unicode_logic_kit/fol/prover9_input.py +1325 -0
- unicode_logic_kit/fol/qml.py +1760 -0
- unicode_logic_kit/fol/qmltp_input.py +525 -0
- unicode_logic_kit/fol/sanitize.py +221 -0
- unicode_logic_kit/fol/serialize.py +79 -0
- unicode_logic_kit/fol/signature.py +1290 -0
- unicode_logic_kit/fol/simplify_check.py +544 -0
- unicode_logic_kit/fol/spans.py +594 -0
- unicode_logic_kit/fol/tptp_input.py +1503 -0
- unicode_logic_kit/fol/tptp_repair.py +941 -0
- unicode_logic_kit/fol/unification.py +157 -0
- unicode_logic_kit/fol/verbalize.py +263 -0
- unicode_logic_kit/hets/__init__.py +163 -0
- unicode_logic_kit/hets/bridge.py +142 -0
- unicode_logic_kit/hets/client.py +748 -0
- unicode_logic_kit/hets/docker.py +420 -0
- unicode_logic_kit/hets/dol.py +712 -0
- unicode_logic_kit/hets/haskell_json.py +355 -0
- unicode_logic_kit/hets/owl_backend.py +794 -0
- unicode_logic_kit/hets/owl_cli.py +598 -0
- unicode_logic_kit/hets/symbols.py +512 -0
- unicode_logic_kit/hol/__init__.py +140 -0
- unicode_logic_kit/hol/_ho_common.py +323 -0
- unicode_logic_kit/hol/_isabelle_binders.py +125 -0
- unicode_logic_kit/hol/classical.py +812 -0
- unicode_logic_kit/hol/deepshallow/__init__.py +45 -0
- unicode_logic_kit/hol/deepshallow/_common.py +177 -0
- unicode_logic_kit/hol/deepshallow/conditional.py +225 -0
- unicode_logic_kit/hol/deepshallow/intuitionistic.py +181 -0
- unicode_logic_kit/hol/deepshallow/modal.py +217 -0
- unicode_logic_kit/hol/deepshallow/qml.py +406 -0
- unicode_logic_kit/hol/deepshallow/relevant.py +206 -0
- unicode_logic_kit/hol/free.py +753 -0
- unicode_logic_kit/hol/goedel.py +336 -0
- unicode_logic_kit/hol/ho_modal.py +1743 -0
- unicode_logic_kit/hol/intuitionistic.py +403 -0
- unicode_logic_kit/hol/isabelle_conditional.py +593 -0
- unicode_logic_kit/hol/isabelle_modal.py +1908 -0
- unicode_logic_kit/hol/isabelle_relevant.py +412 -0
- unicode_logic_kit/hol/isabelle_runner.py +1147 -0
- unicode_logic_kit/hol/isabelle_substructural.py +884 -0
- unicode_logic_kit/hol/lean.py +1018 -0
- unicode_logic_kit/hol/manyvalued.py +921 -0
- unicode_logic_kit/hol/secondorder.py +687 -0
- unicode_logic_kit/hol/thf_modal.py +941 -0
- unicode_logic_kit/hol/thirdorder.py +397 -0
- unicode_logic_kit/ilp/__init__.py +89 -0
- unicode_logic_kit/ilp/readback.py +389 -0
- unicode_logic_kit/ilp/separation.py +153 -0
- unicode_logic_kit/ilp/task.py +730 -0
- unicode_logic_kit/logic.py +163 -0
- unicode_logic_kit/mcp/__init__.py +28 -0
- unicode_logic_kit/mcp/__main__.py +5 -0
- unicode_logic_kit/mcp/chem_tools.py +1031 -0
- unicode_logic_kit/mcp/server.py +2453 -0
- unicode_logic_kit/mcp/syntax_spec.py +681 -0
- unicode_logic_kit/prob/__init__.py +53 -0
- unicode_logic_kit/prob/_bdd.py +225 -0
- unicode_logic_kit/prob/_column_gen.py +668 -0
- unicode_logic_kit/prob/distribution.py +686 -0
- unicode_logic_kit/prob/nilsson.py +470 -0
- unicode_logic_kit/py.typed +0 -0
- unicode_logic_kit/semantics/__init__.py +137 -0
- unicode_logic_kit/semantics/_modal_reject.py +156 -0
- unicode_logic_kit/semantics/action_models.py +466 -0
- unicode_logic_kit/semantics/asp_models.py +1200 -0
- unicode_logic_kit/semantics/conditional.py +580 -0
- unicode_logic_kit/semantics/dynamic_epistemic.py +95 -0
- unicode_logic_kit/semantics/free_logic.py +913 -0
- unicode_logic_kit/semantics/fuzzy.py +384 -0
- unicode_logic_kit/semantics/fuzzy_kripke.py +442 -0
- unicode_logic_kit/semantics/intuitionistic.py +581 -0
- unicode_logic_kit/semantics/kripke.py +1139 -0
- unicode_logic_kit/semantics/manyvalued.py +580 -0
- unicode_logic_kit/semantics/matrix.py +342 -0
- unicode_logic_kit/semantics/model_eval.py +1135 -0
- unicode_logic_kit/semantics/modelfinder.py +1036 -0
- unicode_logic_kit/semantics/nonmonotonic.py +372 -0
- unicode_logic_kit/semantics/relevant.py +331 -0
- unicode_logic_kit/semantics/secondorder.py +657 -0
- unicode_logic_kit/semantics/structures.py +352 -0
- unicode_logic_kit/semantics/tarski.py +975 -0
- unicode_logic_kit/semantics/team.py +315 -0
- unicode_logic_kit/semantics/team_translation.py +416 -0
- unicode_logic_kit/semantics/thirdorder.py +358 -0
- unicode_logic_kit/semantics/tnorm.py +85 -0
- unicode_logic_kit/semantics/truthtable.py +201 -0
- unicode_logic_kit-0.31.0.dist-info/METADATA +333 -0
- unicode_logic_kit-0.31.0.dist-info/RECORD +237 -0
- unicode_logic_kit-0.31.0.dist-info/WHEEL +4 -0
- unicode_logic_kit-0.31.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
"""The Discourse Representation Structure (DRS) data model — the classical Kamp/Reyle core.
|
|
2
|
+
|
|
3
|
+
Discourse Representation Theory (Kamp & Reyle, *From Discourse to Logic*, 1993) represents
|
|
4
|
+
a discourse as a **box**: a set of *discourse referents* (the individuals under discussion)
|
|
5
|
+
plus a set of *conditions* asserted about them. A box can itself appear as an operand of
|
|
6
|
+
another condition (negation, a conditional's antecedent/consequent, a disjunct), which is
|
|
7
|
+
how DRT gets recursive structure and — crucially — a notion of which referents are visible
|
|
8
|
+
where. This module implements exactly the propositional core of that theory: no tense,
|
|
9
|
+
no plurals, no presupposition, no generalized quantifiers beyond the donkey-sentence
|
|
10
|
+
universal reading built into the conditional (see ``unicode_logic_kit.drt.export`` for why
|
|
11
|
+
that reading is forced rather than optional). Those omissions are deliberate scope limits
|
|
12
|
+
of this kit subpackage, not oversights — see ``unicode_logic_kit.drt``'s package docstring.
|
|
13
|
+
|
|
14
|
+
Conditions (five classical variants matching Kamp & Reyle's own inventory, plus
|
|
15
|
+
the two plural-DRT extensions of chapter 4 added in 0.24.0)::
|
|
16
|
+
|
|
17
|
+
Pred(name, args) # an atomic condition, e.g. Pred("Farmer", ("x",))
|
|
18
|
+
Eq(a, b) # a = b
|
|
19
|
+
Neg(drs) # ¬[drs] (negation)
|
|
20
|
+
Impl(antecedent, consequent) # [antecedent] => [consequent] (a "duplex condition")
|
|
21
|
+
Or(left, right) # [left] or [right]
|
|
22
|
+
Card(ref, op, n) # |ref| op n (group cardinality)
|
|
23
|
+
Part(member, group) # member ∈ group (renders/exports as Part_of)
|
|
24
|
+
|
|
25
|
+
The plural pair is deliberately MINIMAL — measured against the ACE corpus, not
|
|
26
|
+
designed in the abstract: ``Card`` + ``Part`` are what collective readings
|
|
27
|
+
("John and Mary lift a table") and counted plurals ("at least 3 men wait")
|
|
28
|
+
actually need, while DISTRIBUTION needs no operator of its own because it is
|
|
29
|
+
the duplex condition applied to membership (``[x | Part_of(x, g)] => [ |
|
|
30
|
+
...]`` — exactly what ACE's "each of" compiles to). What is still absent, and
|
|
31
|
+
still deliberate: no maximality/abstraction (Kamp & Reyle's Σ — "exactly n"
|
|
32
|
+
as a counting quantifier lives at the FORMULA level via
|
|
33
|
+
``unicode_logic_kit.fol.nodes.Count``, see ``unicode_logic_kit.ace.translate``),
|
|
34
|
+
no tense, no presupposition.
|
|
35
|
+
|
|
36
|
+
``DRS(referents, conditions)`` is the box itself: a frozen dataclass pairing a tuple of
|
|
37
|
+
referent names with a tuple of conditions.
|
|
38
|
+
|
|
39
|
+
Referent vs. constant: a single lexical convention, not two vocabularies
|
|
40
|
+
--------------------------------------------------------------------------
|
|
41
|
+
Every string that appears as a ``Pred``/``Eq`` argument is either a *discourse referent*
|
|
42
|
+
(bound by some box's own existential/universal quantifier once translated to FOL) or a
|
|
43
|
+
*constant* (a rigid designator, e.g. a proper name). Rather than tagging each argument with
|
|
44
|
+
its role explicitly, this module reuses the kit's OWN lexical convention for telling a FOL
|
|
45
|
+
Variable from a Constant — see ``unicode_logic_kit/fol/grammars/terminals.lark``::
|
|
46
|
+
|
|
47
|
+
VARIABLE.1: /[a-z][0-9]*/ # single lowercase letter + digits
|
|
48
|
+
NAME.2: /[a-z][a-zA-Z0-9_]*[a-zA-Z][a-zA-Z0-9_]*/ # lowercase-initial, >= 2 LETTERS,
|
|
49
|
+
# `_` in continuation (0.23.0/0.23.1)
|
|
50
|
+
CONSTANT.3: /c_[a-zA-Z0-9]+/ | ... # explicit c_-prefixed form
|
|
51
|
+
|
|
52
|
+
so here: a single lowercase letter optionally followed by digits (``x``, ``y``, ``z1``,
|
|
53
|
+
``e12``, ...) is a REFERENT; anything else that is lowercase-initial and at least two
|
|
54
|
+
characters (``john``, ``daisy``, ...) or carries the explicit ``c_...`` form is a CONSTANT.
|
|
55
|
+
This is a deliberate, load-bearing restriction (not merely descriptive): a single-letter
|
|
56
|
+
name is ALWAYS read as a referent, so a one-character constant is not directly expressible
|
|
57
|
+
in this notation — the parser's quoted-string / SBN's quoted-constant path sanitizes such
|
|
58
|
+
cases into a legal (necessarily >= 2 character) constant name instead of silently colliding
|
|
59
|
+
with the referent namespace (see ``unicode_logic_kit.drt.parser``). ``is_referent`` /
|
|
60
|
+
``is_predicate_name`` / ``is_constant_name`` below are the single source of truth for this
|
|
61
|
+
classification; every other module in this subpackage (and ``unicode_logic_kit.drt.export``'s
|
|
62
|
+
standard translation) uses them rather than re-deriving the rule.
|
|
63
|
+
|
|
64
|
+
A predicate name (``Pred.name``) follows the kit's own PREDICATE convention:
|
|
65
|
+
uppercase-initial, then alphanumerics or underscores — since 0.23.1 the grammar's
|
|
66
|
+
PREDICATE takes NAME's continuation class (which is how ``Has_bond_to`` and the rest
|
|
67
|
+
of the chem vocabulary are writable), and this module follows the same rule.
|
|
68
|
+
|
|
69
|
+
Global referent uniqueness (a deliberate simplification, not part of classical DRT)
|
|
70
|
+
-------------------------------------------------------------------------------------
|
|
71
|
+
Full Kamp/Reyle DRT allows the same referent-like variable name to be reused in disjoint
|
|
72
|
+
boxes (it is a bound variable in each; alpha-renaming makes the reuse harmless). This
|
|
73
|
+
module does NOT allow that: :meth:`DRS.validate` requires every referent name appearing
|
|
74
|
+
anywhere in one DRS tree to be declared by exactly ONE box. This sidesteps alpha-renaming
|
|
75
|
+
bookkeeping entirely (the standard translation in ``export.py`` can use referent names
|
|
76
|
+
directly as FOL variable names with no risk of one quantifier accidentally shadowing
|
|
77
|
+
another's), at the cost of requiring the caller to pick distinct names across the whole
|
|
78
|
+
discourse — a small, explicit, well-documented restriction, very much in the "loud
|
|
79
|
+
refusal over silent misreading" spirit of this kit: a shadowing collision is refused with
|
|
80
|
+
a clear message rather than silently producing a formula whose nested quantifiers reuse a
|
|
81
|
+
name in a way the caller may not have intended.
|
|
82
|
+
|
|
83
|
+
Construction-time vs. final validation
|
|
84
|
+
-----------------------------------------
|
|
85
|
+
A condition's referent-shaped arguments must ultimately refer to an ACCESSIBLE box — the
|
|
86
|
+
classical Kamp/Reyle accessibility relation, defined below. But construction is necessarily
|
|
87
|
+
bottom-up (an inner box, e.g. a conditional's consequent ``[| Beats(x, y)]``, is built
|
|
88
|
+
*before* the ``Impl`` that will make its antecedent's referents ``x``/``y`` accessible to
|
|
89
|
+
it), so at the moment ``DRS(referents=(), conditions=(Pred("Beats", ("x", "y")),))`` is
|
|
90
|
+
constructed there is no way to know yet whether ``x``/``y`` will end up accessible — they
|
|
91
|
+
might never become so, if the caller forgets to wrap it in the right ``Impl``. Rejecting
|
|
92
|
+
this at ``__init__`` would make the classical donkey-sentence construction pattern
|
|
93
|
+
impossible to build piece by piece. So ``__init__`` (on ``DRS`` and on each condition
|
|
94
|
+
type) validates only what is knowable locally and independent of assembly order: shapes,
|
|
95
|
+
types, no duplicate referents *within one box's own tuple*. The GLOBAL accessibility check
|
|
96
|
+
— "is every referent-shaped argument, everywhere in this tree, accessible from the box it
|
|
97
|
+
occurs in?" — is :meth:`DRS.validate`, meant to be called once the whole tree is assembled
|
|
98
|
+
(and is called automatically by ``unicode_logic_kit.drt.export.drs_to_fol`` and
|
|
99
|
+
``unicode_logic_kit.drt.resolve.resolve_anaphora`` before they do anything with the tree).
|
|
100
|
+
|
|
101
|
+
The accessibility relation
|
|
102
|
+
-----------------------------
|
|
103
|
+
For a box ``K`` reached by walking down from the root, the referents ACCESSIBLE to K's own
|
|
104
|
+
direct conditions are exactly:
|
|
105
|
+
|
|
106
|
+
* K's own referents, plus
|
|
107
|
+
* every referent accessible to K's immediate container (the box housing the condition
|
|
108
|
+
that K sits inside, if any) — accessibility is inherited "outer to inner" recursively, plus
|
|
109
|
+
* — the one asymmetric rule, THE donkey-sentence rule — if K is the CONSEQUENT of an
|
|
110
|
+
``Impl(A, K)``, then A's own referents are ALSO accessible to K (but not vice versa, and
|
|
111
|
+
not to anything outside the ``Impl`` condition).
|
|
112
|
+
|
|
113
|
+
Concretely, entering each condition type from a box with accessible set ``acc``:
|
|
114
|
+
|
|
115
|
+
* ``Neg(D)``: D's accessible set is ``acc | D.referents``. D's referents do NOT propagate
|
|
116
|
+
back out — a referent introduced under negation is invisible outside it (the classic "no
|
|
117
|
+
farmer owns a donkey; # it is grey" infelicity).
|
|
118
|
+
* ``Impl(A, B)``: A's accessible set is ``acc | A.referents``. B's accessible set is
|
|
119
|
+
``acc | A.referents | B.referents`` (A's referents leak forward into B — the donkey
|
|
120
|
+
rule — but not backward into A, and neither A's nor B's referents propagate outside the
|
|
121
|
+
``Impl`` condition).
|
|
122
|
+
* ``Or(D1, D2)``: D1's accessible set is ``acc | D1.referents``; D2's is
|
|
123
|
+
``acc | D2.referents`` — independent alternatives, like ``Neg``: neither leaks into the
|
|
124
|
+
other or outside.
|
|
125
|
+
|
|
126
|
+
:func:`walk_boxes` computes this relation once for a whole tree (as an outer-to-inner
|
|
127
|
+
sequence of referent "layers" per box, so :mod:`unicode_logic_kit.drt.resolve` can also use it
|
|
128
|
+
for a recency ordering); :meth:`DRS.validate` is the accessibility checker built on top of it.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
import re
|
|
132
|
+
from dataclasses import dataclass
|
|
133
|
+
from typing import Dict, Iterator, Tuple
|
|
134
|
+
|
|
135
|
+
_REF_RE = re.compile(r"^[a-z][0-9]*$")
|
|
136
|
+
_PRED_RE = re.compile(r"^[A-Z][a-zA-Z0-9_]*$")
|
|
137
|
+
# Lowercase-initial, >= 2 LETTERS somewhere, continuation may include `_`
|
|
138
|
+
# (0.23.0/0.23.1 widened the grammar's NAME the same way; verified against
|
|
139
|
+
# the live parser: a_b, ab_, john_smith are constants; a_1, x_ are not).
|
|
140
|
+
_BARE_CONST_RE = re.compile(r"^[a-z][a-zA-Z0-9_]*[a-zA-Z][a-zA-Z0-9_]*$")
|
|
141
|
+
_EXPLICIT_CONST_RE = re.compile(r"^c_[a-zA-Z0-9]+$")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def is_referent(name: str) -> bool:
|
|
145
|
+
"""True iff ``name`` is a legal discourse-referent name (a single lowercase letter
|
|
146
|
+
optionally followed by digits — ``x``, ``y``, ``z1``, ``e12``, ...; the kit's own
|
|
147
|
+
FOL Variable convention, see the module docstring)."""
|
|
148
|
+
return isinstance(name, str) and bool(_REF_RE.fullmatch(name))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def is_predicate_name(name: str) -> bool:
|
|
152
|
+
"""True iff ``name`` is a legal predicate name: uppercase-initial, then
|
|
153
|
+
alphanumerics or underscores — the kit's own PREDICATE convention. The
|
|
154
|
+
underscore is a CONTINUATION character (0.23.1 widened the grammar's
|
|
155
|
+
PREDICATE to NAME's continuation class precisely so that the chem
|
|
156
|
+
vocabulary's ``Has_bond_to`` is writable); this module tracked the old,
|
|
157
|
+
narrower rule until 0.24.0, which made every underscore predicate the
|
|
158
|
+
fol parser accepts unusable in a DRS."""
|
|
159
|
+
return isinstance(name, str) and bool(_PRED_RE.fullmatch(name))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def is_constant_name(name: str) -> bool:
|
|
163
|
+
"""True iff ``name`` is a legal constant name: either a bare lowercase-initial
|
|
164
|
+
name with at least two LETTERS, underscores allowed in continuation (the kit's
|
|
165
|
+
NAME convention, e.g. ``john``, ``john_smith`` — but not ``a_1``, which has one
|
|
166
|
+
letter and would sit next to the referent namespace), or the explicit ``c_...``
|
|
167
|
+
form (the kit's CONSTANT convention, for names that don't fit NAME)."""
|
|
168
|
+
return isinstance(name, str) and bool(
|
|
169
|
+
_BARE_CONST_RE.fullmatch(name) or _EXPLICIT_CONST_RE.fullmatch(name))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _check_term_name(a, *, context: str) -> None:
|
|
173
|
+
"""Loudly refuse any Pred/Eq argument that is neither a legal referent nor a legal
|
|
174
|
+
constant name (see the module docstring's "Referent vs. constant" section)."""
|
|
175
|
+
if not isinstance(a, str) or not a:
|
|
176
|
+
raise ValueError(f"{context}: argument must be a non-empty string, got {a!r}")
|
|
177
|
+
if is_referent(a) or is_constant_name(a):
|
|
178
|
+
return
|
|
179
|
+
raise ValueError(
|
|
180
|
+
f"{context}: {a!r} is neither a legal referent name (a single lowercase letter "
|
|
181
|
+
f"optionally followed by digits, e.g. 'x', 'y1') nor a legal constant name (a "
|
|
182
|
+
f"lowercase-initial name of >= 2 characters, e.g. 'john', or the explicit "
|
|
183
|
+
f"'c_...' form). An arbitrary/quoted constant must be sanitized first — see "
|
|
184
|
+
f"unicode_logic_kit.drt.parser's quoted-constant handling.")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Condition:
|
|
188
|
+
"""Base class for the five DRS condition variants (see the module docstring)."""
|
|
189
|
+
|
|
190
|
+
def to_dict(self) -> dict:
|
|
191
|
+
raise NotImplementedError
|
|
192
|
+
|
|
193
|
+
def to_box_notation(self) -> str:
|
|
194
|
+
"""Render this condition in the box notation :func:`unicode_logic_kit.drt.parser.parse_drs`
|
|
195
|
+
accepts, as it would appear inside a box's condition list."""
|
|
196
|
+
raise NotImplementedError
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass(frozen=True)
|
|
200
|
+
class Pred(Condition):
|
|
201
|
+
"""An atomic condition: ``name`` applied to one or more referent/constant ``args``.
|
|
202
|
+
|
|
203
|
+
``name`` must be a legal predicate name (uppercase-initial, alphanumeric); each of
|
|
204
|
+
``args`` must be a legal referent or constant name (see the module docstring). At
|
|
205
|
+
least one argument is required — DRT conditions always assert something OF one or
|
|
206
|
+
more discourse referents/constants, so a nullary condition has no discourse content.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
name: str
|
|
210
|
+
args: Tuple[str, ...]
|
|
211
|
+
|
|
212
|
+
def __post_init__(self):
|
|
213
|
+
object.__setattr__(self, "args", tuple(self.args))
|
|
214
|
+
if not is_predicate_name(self.name):
|
|
215
|
+
raise ValueError(
|
|
216
|
+
f"Pred: predicate name {self.name!r} must be uppercase-initial, "
|
|
217
|
+
f"then alphanumerics/underscores (the kit PREDICATE convention, "
|
|
218
|
+
f"e.g. 'Farmer', 'Owns', 'Has_bond_to').")
|
|
219
|
+
if not self.args:
|
|
220
|
+
raise ValueError(
|
|
221
|
+
f"Pred({self.name!r}): needs at least one argument.")
|
|
222
|
+
for a in self.args:
|
|
223
|
+
_check_term_name(a, context=f"Pred({self.name!r}, {self.args!r})")
|
|
224
|
+
|
|
225
|
+
def to_dict(self) -> dict:
|
|
226
|
+
return {"_type": "Pred", "name": self.name, "args": list(self.args)}
|
|
227
|
+
|
|
228
|
+
def to_box_notation(self) -> str:
|
|
229
|
+
return f"{self.name}(" + ", ".join(self.args) + ")"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass(frozen=True)
|
|
233
|
+
class Eq(Condition):
|
|
234
|
+
"""An equality condition ``a = b`` between two referents/constants."""
|
|
235
|
+
|
|
236
|
+
a: str
|
|
237
|
+
b: str
|
|
238
|
+
|
|
239
|
+
def __post_init__(self):
|
|
240
|
+
_check_term_name(self.a, context=f"Eq({self.a!r}, {self.b!r})")
|
|
241
|
+
_check_term_name(self.b, context=f"Eq({self.a!r}, {self.b!r})")
|
|
242
|
+
|
|
243
|
+
def to_dict(self) -> dict:
|
|
244
|
+
return {"_type": "Eq", "a": self.a, "b": self.b}
|
|
245
|
+
|
|
246
|
+
def to_box_notation(self) -> str:
|
|
247
|
+
return f"{self.a} = {self.b}"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
#: The comparison operators :class:`Card` accepts, in kit spelling.
|
|
251
|
+
CARD_OPS = ("=", ">=", "<=", ">", "<")
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@dataclass(frozen=True)
|
|
255
|
+
class Card(Condition):
|
|
256
|
+
"""A group-cardinality condition: ``|ref| op n``.
|
|
257
|
+
|
|
258
|
+
``ref`` is the group's referent (or constant), ``op`` one of
|
|
259
|
+
:data:`CARD_OPS`, ``n`` a non-negative int. Semantics via the standard
|
|
260
|
+
translation (``unicode_logic_kit.drt.export``): the number of ``x`` with
|
|
261
|
+
``Part_of(x, ref)`` stands in ``op`` to ``n``, rendered as the kit's
|
|
262
|
+
counting quantifier ``Count`` — so ``Card`` inherits ``Count``'s exact
|
|
263
|
+
first-order expansion on every backend. ``Card(ref, "<", 0)`` is refused
|
|
264
|
+
at construction: nothing has fewer than zero parts, and a condition that
|
|
265
|
+
is FALSE by spelling is a mistake, not a statement.
|
|
266
|
+
|
|
267
|
+
Box notation: ``Card(g, >=, 3)`` — unambiguous against a ``Pred`` named
|
|
268
|
+
``Card`` because no legal ``Pred`` argument can be a bare comparison
|
|
269
|
+
operator or integer.
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
ref: str
|
|
273
|
+
op: str
|
|
274
|
+
n: int
|
|
275
|
+
|
|
276
|
+
def __post_init__(self):
|
|
277
|
+
_check_term_name(self.ref, context=f"Card({self.ref!r}, ...)")
|
|
278
|
+
if self.op not in CARD_OPS:
|
|
279
|
+
raise ValueError(
|
|
280
|
+
f"Card: op {self.op!r} must be one of {CARD_OPS}.")
|
|
281
|
+
if not isinstance(self.n, int) or isinstance(self.n, bool) or self.n < 0:
|
|
282
|
+
raise ValueError(f"Card: n must be a non-negative int, got {self.n!r}.")
|
|
283
|
+
if self.op == "<" and self.n == 0:
|
|
284
|
+
raise ValueError(
|
|
285
|
+
"Card: '< 0' is unsatisfiable by spelling — nothing has "
|
|
286
|
+
"fewer than zero parts.")
|
|
287
|
+
|
|
288
|
+
def to_dict(self) -> dict:
|
|
289
|
+
return {"_type": "Card", "ref": self.ref, "op": self.op, "n": self.n}
|
|
290
|
+
|
|
291
|
+
def to_box_notation(self) -> str:
|
|
292
|
+
return f"Card({self.ref}, {self.op}, {self.n})"
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@dataclass(frozen=True)
|
|
296
|
+
class Part(Condition):
|
|
297
|
+
"""A membership condition: ``member`` is a part of the group ``group``.
|
|
298
|
+
|
|
299
|
+
Renders — and exports — as the distinguished binary predicate
|
|
300
|
+
``Part_of``, chosen over the bare ``Part`` because ACE's noun "part"
|
|
301
|
+
would otherwise produce a UNARY ``Part`` predicate next to a binary one,
|
|
302
|
+
and one symbol with two arities breaks every ASCII export route (TPTP,
|
|
303
|
+
Prover9, z3 declarations). A hand-written ``Pred("Part_of", (m, g))``
|
|
304
|
+
means exactly the same thing at the FOL level; the typed condition
|
|
305
|
+
exists so the export and the plural machinery can FIND membership
|
|
306
|
+
without string-matching predicate names.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
member: str
|
|
310
|
+
group: str
|
|
311
|
+
|
|
312
|
+
def __post_init__(self):
|
|
313
|
+
_check_term_name(self.member, context=f"Part({self.member!r}, {self.group!r})")
|
|
314
|
+
_check_term_name(self.group, context=f"Part({self.member!r}, {self.group!r})")
|
|
315
|
+
|
|
316
|
+
def to_dict(self) -> dict:
|
|
317
|
+
return {"_type": "Part", "member": self.member, "group": self.group}
|
|
318
|
+
|
|
319
|
+
def to_box_notation(self) -> str:
|
|
320
|
+
return f"Part_of({self.member}, {self.group})"
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@dataclass(frozen=True)
|
|
324
|
+
class Neg(Condition):
|
|
325
|
+
"""Negation of a sub-DRS: ``¬[drs]``. ``drs``'s own referents are accessible within
|
|
326
|
+
``drs`` but do not escape (see the module docstring's accessibility relation)."""
|
|
327
|
+
|
|
328
|
+
drs: "DRS"
|
|
329
|
+
|
|
330
|
+
def __post_init__(self):
|
|
331
|
+
if not isinstance(self.drs, DRS):
|
|
332
|
+
raise TypeError(f"Neg: operand must be a DRS, got {type(self.drs).__name__}")
|
|
333
|
+
|
|
334
|
+
def to_dict(self) -> dict:
|
|
335
|
+
return {"_type": "Neg", "drs": self.drs.to_dict()}
|
|
336
|
+
|
|
337
|
+
def to_box_notation(self) -> str:
|
|
338
|
+
return f"~{self.drs.to_box_notation()}"
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@dataclass(frozen=True)
|
|
342
|
+
class Impl(Condition):
|
|
343
|
+
"""A duplex (conditional) condition ``[antecedent] => [consequent]`` — THE
|
|
344
|
+
donkey-sentence construct: ``antecedent``'s referents are accessible in
|
|
345
|
+
``consequent`` (but nowhere else) — see the module docstring."""
|
|
346
|
+
|
|
347
|
+
antecedent: "DRS"
|
|
348
|
+
consequent: "DRS"
|
|
349
|
+
|
|
350
|
+
def __post_init__(self):
|
|
351
|
+
for field_name, val in (("antecedent", self.antecedent), ("consequent", self.consequent)):
|
|
352
|
+
if not isinstance(val, DRS):
|
|
353
|
+
raise TypeError(f"Impl: {field_name} must be a DRS, got {type(val).__name__}")
|
|
354
|
+
|
|
355
|
+
def to_dict(self) -> dict:
|
|
356
|
+
return {"_type": "Impl", "antecedent": self.antecedent.to_dict(),
|
|
357
|
+
"consequent": self.consequent.to_dict()}
|
|
358
|
+
|
|
359
|
+
def to_box_notation(self) -> str:
|
|
360
|
+
return f"{self.antecedent.to_box_notation()} -> {self.consequent.to_box_notation()}"
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@dataclass(frozen=True)
|
|
364
|
+
class Or(Condition):
|
|
365
|
+
"""A disjunctive condition ``[left] or [right]``; each side's referents are
|
|
366
|
+
accessible only within that side (see the module docstring)."""
|
|
367
|
+
|
|
368
|
+
left: "DRS"
|
|
369
|
+
right: "DRS"
|
|
370
|
+
|
|
371
|
+
def __post_init__(self):
|
|
372
|
+
for field_name, val in (("left", self.left), ("right", self.right)):
|
|
373
|
+
if not isinstance(val, DRS):
|
|
374
|
+
raise TypeError(f"Or: {field_name} must be a DRS, got {type(val).__name__}")
|
|
375
|
+
|
|
376
|
+
def to_dict(self) -> dict:
|
|
377
|
+
return {"_type": "Or", "left": self.left.to_dict(), "right": self.right.to_dict()}
|
|
378
|
+
|
|
379
|
+
def to_box_notation(self) -> str:
|
|
380
|
+
return f"{self.left.to_box_notation()} ∨ {self.right.to_box_notation()}"
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@dataclass(frozen=True)
|
|
384
|
+
class DRS:
|
|
385
|
+
"""A Discourse Representation Structure (a "box"): ``referents`` declared in this
|
|
386
|
+
box, and ``conditions`` asserted about them (and about whatever is accessible from
|
|
387
|
+
an enclosing box — see the module docstring).
|
|
388
|
+
|
|
389
|
+
Construction validates only local shape (each referent is a legal, box-locally-unique
|
|
390
|
+
referent name; each condition is a :class:`Condition` instance) — NOT cross-box
|
|
391
|
+
accessibility, which construction order makes impossible to check here (see the module
|
|
392
|
+
docstring). Call :meth:`validate` once a whole tree is assembled.
|
|
393
|
+
"""
|
|
394
|
+
|
|
395
|
+
referents: Tuple[str, ...]
|
|
396
|
+
conditions: Tuple[Condition, ...]
|
|
397
|
+
|
|
398
|
+
def __post_init__(self):
|
|
399
|
+
object.__setattr__(self, "referents", tuple(self.referents))
|
|
400
|
+
object.__setattr__(self, "conditions", tuple(self.conditions))
|
|
401
|
+
seen = set()
|
|
402
|
+
for r in self.referents:
|
|
403
|
+
if not is_referent(r):
|
|
404
|
+
raise ValueError(
|
|
405
|
+
f"DRS: referent {r!r} is not a legal referent name (a single "
|
|
406
|
+
f"lowercase letter optionally followed by digits, e.g. 'x', 'y1').")
|
|
407
|
+
if r in seen:
|
|
408
|
+
raise ValueError(
|
|
409
|
+
f"DRS: duplicate referent {r!r} within one box's own referent "
|
|
410
|
+
f"list {self.referents!r}.")
|
|
411
|
+
seen.add(r)
|
|
412
|
+
for c in self.conditions:
|
|
413
|
+
if not isinstance(c, Condition):
|
|
414
|
+
raise TypeError(
|
|
415
|
+
f"DRS: condition {c!r} is not a Condition instance "
|
|
416
|
+
f"(got {type(c).__name__}).")
|
|
417
|
+
|
|
418
|
+
def to_dict(self) -> dict:
|
|
419
|
+
return {"_type": "DRS", "referents": list(self.referents),
|
|
420
|
+
"conditions": [c.to_dict() for c in self.conditions]}
|
|
421
|
+
|
|
422
|
+
def to_box_notation(self) -> str:
|
|
423
|
+
"""Render this DRS in the box notation :func:`unicode_logic_kit.drt.parser.parse_drs`
|
|
424
|
+
accepts: ``parse_drs(drs.to_box_notation())`` reproduces an equal ``DRS`` (see
|
|
425
|
+
that module for the exact grammar; a top-level ``Neg``/``Impl``/``Or`` — i.e. a
|
|
426
|
+
DRS built as ``DRS((), (cond,))`` to wrap a bare duplex/negated condition — round
|
|
427
|
+
trips to an equal value, though through one extra bracket layer, since
|
|
428
|
+
``parse_drs`` always accepts ``[ | <that condition>]`` as a plain box)."""
|
|
429
|
+
refs = ", ".join(self.referents)
|
|
430
|
+
conds = ", ".join(c.to_box_notation() for c in self.conditions)
|
|
431
|
+
return f"[{refs} | {conds}]"
|
|
432
|
+
|
|
433
|
+
def validate(self) -> bool:
|
|
434
|
+
"""Check the classical Kamp/Reyle accessibility relation over the whole tree
|
|
435
|
+
rooted at ``self`` (see the module docstring), plus global referent-name
|
|
436
|
+
uniqueness (this kit's deliberate simplification, see the module docstring).
|
|
437
|
+
|
|
438
|
+
Raises:
|
|
439
|
+
ValueError: a referent name is declared by more than one box, or some
|
|
440
|
+
condition's referent-shaped argument is not accessible where it occurs
|
|
441
|
+
(message names the referent, the condition, and the accessible set).
|
|
442
|
+
Returns:
|
|
443
|
+
True on success (never returns False — failure is always an exception, per
|
|
444
|
+
this kit's "loud refusal over silent misreading" convention).
|
|
445
|
+
"""
|
|
446
|
+
boxes = list(walk_boxes(self))
|
|
447
|
+
|
|
448
|
+
declared: Dict[str, "DRS"] = {}
|
|
449
|
+
for box, _ in boxes:
|
|
450
|
+
for r in box.referents:
|
|
451
|
+
if r in declared and declared[r] is not box:
|
|
452
|
+
raise ValueError(
|
|
453
|
+
f"DRS.validate: referent {r!r} is declared in more than one "
|
|
454
|
+
f"box in this tree. This DRT implementation requires globally "
|
|
455
|
+
f"distinct referent names (no shadowing/alpha-renaming, see "
|
|
456
|
+
f"the module docstring) — rename one of the occurrences.")
|
|
457
|
+
declared[r] = box
|
|
458
|
+
|
|
459
|
+
for box, layers in boxes:
|
|
460
|
+
acc = frozenset(n for layer in layers for n in layer)
|
|
461
|
+
for cond in box.conditions:
|
|
462
|
+
if isinstance(cond, Pred):
|
|
463
|
+
args = cond.args
|
|
464
|
+
elif isinstance(cond, Eq):
|
|
465
|
+
args = (cond.a, cond.b)
|
|
466
|
+
elif isinstance(cond, Card):
|
|
467
|
+
args = (cond.ref,)
|
|
468
|
+
elif isinstance(cond, Part):
|
|
469
|
+
args = (cond.member, cond.group)
|
|
470
|
+
else:
|
|
471
|
+
continue
|
|
472
|
+
for a in args:
|
|
473
|
+
if is_referent(a) and a not in acc:
|
|
474
|
+
raise ValueError(
|
|
475
|
+
f"DRS.validate: referent {a!r} in condition "
|
|
476
|
+
f"{cond.to_box_notation()!r} is not accessible at this "
|
|
477
|
+
f"point (accessible referents here: {sorted(acc)!r}). See "
|
|
478
|
+
f"the accessibility relation in the nodes module docstring.")
|
|
479
|
+
return True
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def walk_boxes(
|
|
483
|
+
drs: DRS, _layers: Tuple[Tuple[str, ...], ...] = ()
|
|
484
|
+
) -> Iterator[Tuple[DRS, Tuple[Tuple[str, ...], ...]]]:
|
|
485
|
+
"""Yield every ``(box, layers)`` pair reachable from ``drs`` (``drs`` itself first,
|
|
486
|
+
pre-order), where ``layers`` is the sequence of referent tuples — outermost first,
|
|
487
|
+
``box``'s own referents last — that together make up the referents ACCESSIBLE to
|
|
488
|
+
``box``'s own direct conditions per the classical accessibility relation (see the
|
|
489
|
+
module docstring). ``frozenset(n for layer in layers for n in layer)`` is the flat
|
|
490
|
+
accessible SET; the ordered form is kept so
|
|
491
|
+
:mod:`unicode_logic_kit.drt.resolve` can rank candidates by recency (innermost layer,
|
|
492
|
+
then rightmost position within a layer, is "most recent").
|
|
493
|
+
"""
|
|
494
|
+
if not isinstance(drs, DRS):
|
|
495
|
+
raise TypeError(f"walk_boxes: expected a DRS, got {type(drs).__name__}")
|
|
496
|
+
my_layers = _layers + (drs.referents,)
|
|
497
|
+
yield drs, my_layers
|
|
498
|
+
for cond in drs.conditions:
|
|
499
|
+
if isinstance(cond, Neg):
|
|
500
|
+
yield from walk_boxes(cond.drs, my_layers)
|
|
501
|
+
elif isinstance(cond, Impl):
|
|
502
|
+
yield from walk_boxes(cond.antecedent, my_layers)
|
|
503
|
+
yield from walk_boxes(cond.consequent, my_layers + (cond.antecedent.referents,))
|
|
504
|
+
elif isinstance(cond, Or):
|
|
505
|
+
yield from walk_boxes(cond.left, my_layers)
|
|
506
|
+
yield from walk_boxes(cond.right, my_layers)
|