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,666 @@
|
|
|
1
|
+
"""ACE-DRS → kit DRS: the pure fragment, with a per-condition verdict.
|
|
2
|
+
|
|
3
|
+
The reader (:mod:`unicode_logic_kit.ace.drs_reader`) delivers APE's DRS
|
|
4
|
+
verbatim; this module decides, condition by condition, what the kit's
|
|
5
|
+
classical DRS core (:mod:`unicode_logic_kit.drt.nodes`: ``Pred``/``Eq``/
|
|
6
|
+
``Neg``/``Impl``/``Or``) can carry — and REFUSES the rest by name, with a
|
|
7
|
+
reason and the milestone that will change the verdict. A sentence maps
|
|
8
|
+
either completely or not at all: there is no partial DRS with conditions
|
|
9
|
+
silently dropped, because a DRS missing one condition of the original does
|
|
10
|
+
not mean "most of" the original — it means something else.
|
|
11
|
+
|
|
12
|
+
Vocabulary — what a mapped condition becomes
|
|
13
|
+
--------------------------------------------
|
|
14
|
+
Nouns, verbs, adjectives, adverbs and prepositions become kit predicates,
|
|
15
|
+
uppercased into the kit convention. Events stay, neo-Davidsonian::
|
|
16
|
+
|
|
17
|
+
object(A,man,countable,na,eq,1) -> Man(x1)
|
|
18
|
+
predicate(B,see,A,C) -> See(e1, x1, x2)
|
|
19
|
+
modifier_pp(B,in,G) -> In(e1, x3)
|
|
20
|
+
modifier_adv(B,loudly,pos) -> Loudly(e1)
|
|
21
|
+
property(A,rich,pos) -> Rich(x1)
|
|
22
|
+
property(A,tall,comp_than,B) -> Tall_comp_than(x1, x2)
|
|
23
|
+
relation(A,of,B) -> Of(x1, x2)
|
|
24
|
+
has_part(G,named('John')) -> Part_of(john, g1)
|
|
25
|
+
object(G,na,countable,na,eq,2) -> Card(g1, =, 2)
|
|
26
|
+
object(G,man,countable,na,geq,3) -> Card(g1, >=, 3),
|
|
27
|
+
[x2 | Part_of(x2, g1)] => [ | Man(x2)]
|
|
28
|
+
|
|
29
|
+
Since 0.24.0 the plural shapes land on the DRS core's ``Card``/``Part``
|
|
30
|
+
conditions: a coordination group contributes its cardinality, a counted
|
|
31
|
+
plural additionally distributes its noun over the members via a membership
|
|
32
|
+
duplex — while the group referent itself stays a term for the verb, which
|
|
33
|
+
is ACE's COLLECTIVE reading of the unmarked plural ("at least 3 men wait"
|
|
34
|
+
asserts a waiting group of >= 3 men, not three individual waits; "each of"
|
|
35
|
+
compiles to the duplex and distributes, exactly as APE hands it over).
|
|
36
|
+
|
|
37
|
+
A non-``pos`` degree is folded into the predicate NAME (``Tall_comp_than``)
|
|
38
|
+
rather than dropped — no morphology is attempted ("taller" is never
|
|
39
|
+
reconstructed from ``tall``), and the underscore forms are legal kit
|
|
40
|
+
predicates since 0.23.1. The copula follows Attempto's own reference
|
|
41
|
+
translation, measured on its TPTP output: ``predicate(B,be,X,Y)`` becomes
|
|
42
|
+
the EQUALITY ``X = Y`` and the be-event referent is dropped — but only when
|
|
43
|
+
that referent occurs nowhere else in the whole DRS; if something modifies
|
|
44
|
+
the be-event (nothing in the measured corpus does), the guard keeps it as a
|
|
45
|
+
``Be(e, x, y)`` predicate instead of silently orphaning the modifier.
|
|
46
|
+
|
|
47
|
+
Referents are renamed to the kit's convention by ROLE, in order of
|
|
48
|
+
declaration: events (first argument of a ``predicate`` condition) become
|
|
49
|
+
``e1, e2, …``, group referents (a ``na``-noun ``object`` or the whole of a
|
|
50
|
+
``has_part``) become ``g1, …``, everything else ``x1, …``. Proper names
|
|
51
|
+
become kit constants (``named('John')`` → ``john``); values become ``c_``
|
|
52
|
+
constants (``int(30)`` → ``c_30``, ``string('Johnny')`` → ``c_Johnny``) —
|
|
53
|
+
each table is deterministic, collision-checked, and returned on the result.
|
|
54
|
+
|
|
55
|
+
What is refused, and where it goes
|
|
56
|
+
-----------------------------------
|
|
57
|
+
- **modal boxes** — no modal conditions in a classical DRS → ACE-3, done:
|
|
58
|
+
use :func:`unicode_logic_kit.ace.translate.ace_to_formula`;
|
|
59
|
+
- **question / query** — a question is not an assertion → likewise
|
|
60
|
+
``ace_to_formula``;
|
|
61
|
+
- **command** — no imperative semantics decided (no milestone: undecided);
|
|
62
|
+
- **~ (negation as failure)** — not classical negation (no milestone);
|
|
63
|
+
- **the [...] list condition** — the maximality reading of ``exactly``/``at
|
|
64
|
+
most`` is a counting quantifier over a compound scope, a FORMULA-level
|
|
65
|
+
construct → ``ace_to_formula`` carries it (the distributive counting
|
|
66
|
+
reading, documented there);
|
|
67
|
+
- **formula / expr** — arithmetic terms have no place in a DRS condition's
|
|
68
|
+
referent/constant arguments → ``ace_to_formula`` carries it.
|
|
69
|
+
|
|
70
|
+
Everything else unseen (a functor or arity outside the measured corpus)
|
|
71
|
+
is refused as exactly that — "not in the measured corpus" — rather than
|
|
72
|
+
guessed at.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
from __future__ import annotations
|
|
76
|
+
|
|
77
|
+
import re
|
|
78
|
+
from dataclasses import dataclass
|
|
79
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
80
|
+
|
|
81
|
+
from ..drt.nodes import DRS, Condition, Eq, Impl, Neg, Or, Pred
|
|
82
|
+
from .drs_reader import (
|
|
83
|
+
AceAtom, AceCommand, AceCondition, AceCondList, AceDrs, AceExpr, AceImpl,
|
|
84
|
+
AceInt, AceModal, AceNaf, AceNamed, AceNeg, AceOr, AceQuestion, AceReal,
|
|
85
|
+
AceString, AceTerm, AceTermApp, AceVar, parse_ape_drs,
|
|
86
|
+
)
|
|
87
|
+
from .runner import AceError, AceParseError, run_ape
|
|
88
|
+
|
|
89
|
+
__all__ = [
|
|
90
|
+
"map_ace_drs", "ace_to_drs", "DrsMapping", "ConditionReport",
|
|
91
|
+
"condition_statistics", "AceUnsupportedError",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class AceUnsupportedError(AceError):
|
|
96
|
+
"""The text is ACE, but at least one of its DRS conditions is outside
|
|
97
|
+
what the requested route can carry. ``rows`` holds the FULL per-condition
|
|
98
|
+
report — the unsupported entries name the reason and, where one exists,
|
|
99
|
+
the milestone that will change the verdict."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, message: str, rows: Tuple["ConditionReport", ...]):
|
|
102
|
+
super().__init__(message)
|
|
103
|
+
self.rows = rows
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def unsupported(self) -> Tuple["ConditionReport", ...]:
|
|
107
|
+
return tuple(r for r in self.rows if r.verdict == "unsupported")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True)
|
|
111
|
+
class ConditionReport:
|
|
112
|
+
"""One condition's verdict. ``condition`` is APE's own rendering (the
|
|
113
|
+
sub-boxes of a complex condition elided to ``…`` — their conditions get
|
|
114
|
+
rows of their own), ``target`` the kit rendering when mapped,
|
|
115
|
+
``reason``/``milestone`` the refusal when not. ``sentence``/``token``
|
|
116
|
+
point back into the ACE text for atomic conditions; complex shells and
|
|
117
|
+
implicit conditions carry ``None``."""
|
|
118
|
+
|
|
119
|
+
condition: str
|
|
120
|
+
verdict: str # "mapped" | "unsupported"
|
|
121
|
+
target: str = ""
|
|
122
|
+
reason: str = ""
|
|
123
|
+
milestone: str = ""
|
|
124
|
+
sentence: Optional[int] = None
|
|
125
|
+
token: Optional[int] = None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True)
|
|
129
|
+
class DrsMapping:
|
|
130
|
+
"""The result of :func:`map_ace_drs`.
|
|
131
|
+
|
|
132
|
+
``drs`` is the kit DRS when EVERY condition mapped (``complete``), else
|
|
133
|
+
``None`` — never a partial structure. ``rows`` is the per-condition
|
|
134
|
+
report either way. ``referents`` and ``constants`` are the two renaming
|
|
135
|
+
tables (APE's name / the source term's rendering → kit name), returned
|
|
136
|
+
so a caller can trace any kit symbol back to its origin."""
|
|
137
|
+
|
|
138
|
+
drs: Optional[DRS]
|
|
139
|
+
rows: Tuple[ConditionReport, ...]
|
|
140
|
+
referents: Dict[str, str]
|
|
141
|
+
constants: Dict[str, str]
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def complete(self) -> bool:
|
|
145
|
+
return self.drs is not None
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def unsupported(self) -> Tuple[ConditionReport, ...]:
|
|
149
|
+
return tuple(r for r in self.rows if r.verdict == "unsupported")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def condition_statistics(mappings) -> Dict[Tuple[str, str, str], int]:
|
|
153
|
+
"""Aggregate report rows over many mappings — the ACE-5 data basis.
|
|
154
|
+
|
|
155
|
+
Keys are ``(condition label, verdict, milestone)``; the label is the
|
|
156
|
+
functor with arity for atoms (``object/6``) and the bare functor for
|
|
157
|
+
complex shells. Feed it ``map_ace_drs`` results over a corpus and the
|
|
158
|
+
counts say exactly which core extension pays for how many sentences.
|
|
159
|
+
"""
|
|
160
|
+
counts: Dict[Tuple[str, str, str], int] = {}
|
|
161
|
+
for mapping in mappings:
|
|
162
|
+
for row in mapping.rows:
|
|
163
|
+
key = (_row_label(row), row.verdict, row.milestone)
|
|
164
|
+
counts[key] = counts.get(key, 0) + 1
|
|
165
|
+
return counts
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _row_label(row: ConditionReport) -> str:
|
|
169
|
+
# Strip the -S/T index an atomic render carries before reading the
|
|
170
|
+
# arity off the argument list.
|
|
171
|
+
body = re.sub(r"-\d+/(?:\d+|'')$", "", row.condition)
|
|
172
|
+
head = body.split("(", 1)[0]
|
|
173
|
+
if "…" in body or not body.endswith(")"):
|
|
174
|
+
return head or body
|
|
175
|
+
inner = body[len(head) + 1:-1]
|
|
176
|
+
depth = 0
|
|
177
|
+
arity = 1 if inner else 0
|
|
178
|
+
for ch in inner:
|
|
179
|
+
if ch in "([":
|
|
180
|
+
depth += 1
|
|
181
|
+
elif ch in ")]":
|
|
182
|
+
depth -= 1
|
|
183
|
+
elif ch == "," and depth == 0:
|
|
184
|
+
arity += 1
|
|
185
|
+
return f"{head}/{arity}"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
# Renaming
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
_REFERENT_SHAPE = re.compile(r"^[a-z][0-9]*$")
|
|
193
|
+
_ALNUM = re.compile(r"[^a-zA-Z0-9]+")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _named_constant(name: str) -> str:
|
|
197
|
+
"""The proper-name rule, shared with the differential's alignment side:
|
|
198
|
+
``John`` → ``john``; a name that would land in the kit's single-letter
|
|
199
|
+
REFERENT namespace (``E1``) takes the explicit ``c_`` form instead."""
|
|
200
|
+
base = _ALNUM.sub("", name)
|
|
201
|
+
base = base[0].lower() + base[1:] if base else "name"
|
|
202
|
+
if _REFERENT_SHAPE.match(base):
|
|
203
|
+
base = "c_" + (_ALNUM.sub("", name) or "name")
|
|
204
|
+
return base
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class _Renamer:
|
|
208
|
+
"""Deterministic APE-name → kit-name tables for referents and constants.
|
|
209
|
+
|
|
210
|
+
Role detection is a PRE-pass over the whole tree (an event referent is
|
|
211
|
+
one that appears as the first argument of any ``predicate`` condition;
|
|
212
|
+
a group referent as the ``na``-noun of an ``object`` or the whole of a
|
|
213
|
+
``has_part``), so a referent's kit name never depends on which condition
|
|
214
|
+
happens to be visited first.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
def __init__(self, root: AceDrs):
|
|
218
|
+
events, groups = set(), set()
|
|
219
|
+
for cond in root.walk_conditions():
|
|
220
|
+
if not isinstance(cond, AceAtom):
|
|
221
|
+
continue
|
|
222
|
+
if cond.functor == "predicate" and cond.args and isinstance(
|
|
223
|
+
cond.args[0], AceVar):
|
|
224
|
+
events.add(cond.args[0].name)
|
|
225
|
+
if (cond.functor == "object" and len(cond.args) == 6
|
|
226
|
+
and isinstance(cond.args[0], AceVar)):
|
|
227
|
+
_ref, noun, cls, _unit, op, count = cond.args
|
|
228
|
+
# A coordination group (na-noun) or a counted plural — both
|
|
229
|
+
# denote GROUPS, so both take g-names for traceability.
|
|
230
|
+
if noun == "na" or (op in _ACE_OPS and cls != "mass"
|
|
231
|
+
and not (op == "eq" and count == 1)):
|
|
232
|
+
groups.add(cond.args[0].name)
|
|
233
|
+
if (cond.functor == "has_part" and cond.args
|
|
234
|
+
and isinstance(cond.args[0], AceVar)):
|
|
235
|
+
groups.add(cond.args[0].name)
|
|
236
|
+
self._events, self._groups = events, groups
|
|
237
|
+
self.referents: Dict[str, str] = {}
|
|
238
|
+
self.constants: Dict[str, str] = {}
|
|
239
|
+
self._counters = {"e": 0, "g": 0, "x": 0}
|
|
240
|
+
for box in root.walk_boxes():
|
|
241
|
+
for ref in box.referents:
|
|
242
|
+
self._assign(ref.name)
|
|
243
|
+
|
|
244
|
+
def _assign(self, ape_name: str) -> None:
|
|
245
|
+
if ape_name in self.referents:
|
|
246
|
+
return
|
|
247
|
+
prefix = ("e" if ape_name in self._events
|
|
248
|
+
else "g" if ape_name in self._groups else "x")
|
|
249
|
+
self._counters[prefix] += 1
|
|
250
|
+
self.referents[ape_name] = f"{prefix}{self._counters[prefix]}"
|
|
251
|
+
|
|
252
|
+
def fresh(self, prefix: str = "x") -> str:
|
|
253
|
+
"""A synthetic referent no APE variable maps to — the member variable
|
|
254
|
+
of a plural object's membership duplex, or a counting variable. Uses
|
|
255
|
+
the same per-prefix counters as :meth:`_assign`, so synthetic and
|
|
256
|
+
mapped names share one numbering and cannot collide."""
|
|
257
|
+
self._counters[prefix] = self._counters.get(prefix, 0) + 1
|
|
258
|
+
name = f"{prefix}{self._counters[prefix]}"
|
|
259
|
+
while name in self.referents.values():
|
|
260
|
+
self._counters[prefix] += 1
|
|
261
|
+
name = f"{prefix}{self._counters[prefix]}"
|
|
262
|
+
return name
|
|
263
|
+
|
|
264
|
+
def referent(self, var: AceVar) -> str:
|
|
265
|
+
if var.name not in self.referents:
|
|
266
|
+
# A variable used but never declared would be an APE invariant
|
|
267
|
+
# violation; name it visibly rather than crashing.
|
|
268
|
+
self._assign(var.name)
|
|
269
|
+
return self.referents[var.name]
|
|
270
|
+
|
|
271
|
+
def constant(self, term: Union[AceNamed, AceInt, AceReal, AceString]) -> str:
|
|
272
|
+
key = term.render()
|
|
273
|
+
if key in self.constants:
|
|
274
|
+
return self.constants[key]
|
|
275
|
+
if isinstance(term, AceNamed):
|
|
276
|
+
base = _named_constant(term.name)
|
|
277
|
+
elif isinstance(term, AceInt):
|
|
278
|
+
base = "c_" + (str(term.value) if term.value >= 0
|
|
279
|
+
else "m" + str(-term.value))
|
|
280
|
+
elif isinstance(term, AceReal):
|
|
281
|
+
base = "c_" + ("m" if term.value < 0 else "") + str(
|
|
282
|
+
abs(term.value)).replace(".", "p")
|
|
283
|
+
else: # AceString
|
|
284
|
+
cleaned = _ALNUM.sub("", term.value)
|
|
285
|
+
base = "c_" + (cleaned if cleaned else "str")
|
|
286
|
+
candidate, n = base, 1
|
|
287
|
+
while candidate in self.constants.values():
|
|
288
|
+
n += 1
|
|
289
|
+
candidate = f"{base}{n}" if not base.startswith("c_") \
|
|
290
|
+
else f"{base}x{n}"
|
|
291
|
+
self.constants[key] = candidate
|
|
292
|
+
return candidate
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
#: APE's cardinality operators in kit spelling (drt.CARD_OPS). ``na`` never
|
|
296
|
+
#: reaches this table (mass nouns are handled before it).
|
|
297
|
+
_ACE_OPS = {"eq": "=", "geq": ">=", "leq": "<=", "exactly": "=",
|
|
298
|
+
"greater": ">", "less": "<"}
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _kit_predicate(name: str) -> str:
|
|
302
|
+
"""``man`` → ``Man``; anything non-alphanumeric becomes ``_`` (legal in
|
|
303
|
+
kit predicates since 0.23.1, e.g. the chem vocabulary's Has_bond_to)."""
|
|
304
|
+
cleaned = re.sub(r"[^a-zA-Z0-9_]+", "_", name).strip("_") or "P"
|
|
305
|
+
return cleaned[0].upper() + cleaned[1:]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _degree_name(base: str, degree: str) -> str:
|
|
309
|
+
return _kit_predicate(base) if degree == "pos" \
|
|
310
|
+
else f"{_kit_predicate(base)}_{degree}"
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# ---------------------------------------------------------------------------
|
|
314
|
+
# Atomic-condition mapping (shared with the ACE-3 formula route)
|
|
315
|
+
# ---------------------------------------------------------------------------
|
|
316
|
+
|
|
317
|
+
@dataclass(frozen=True)
|
|
318
|
+
class _Unsupported:
|
|
319
|
+
reason: str
|
|
320
|
+
milestone: str = ""
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
#: ("pred", name, args) | ("eq", left, right) | _Unsupported. The tuple
|
|
324
|
+
#: forms carry STRINGS (kit referent/constant names) so both consumers —
|
|
325
|
+
#: drt conditions here, fol atoms in .translate — build their own nodes.
|
|
326
|
+
_Mapped = Union[Tuple[str, str, Tuple[str, ...]], Tuple[str, str, str],
|
|
327
|
+
_Unsupported]
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _term_name(term: AceTerm, renamer: _Renamer) -> Union[str, _Unsupported]:
|
|
331
|
+
if isinstance(term, AceVar):
|
|
332
|
+
return renamer.referent(term)
|
|
333
|
+
if isinstance(term, (AceNamed, AceInt, AceReal, AceString)):
|
|
334
|
+
return renamer.constant(term)
|
|
335
|
+
if isinstance(term, AceExpr):
|
|
336
|
+
return _Unsupported("an arithmetic expression term", "ACE-4")
|
|
337
|
+
if isinstance(term, AceTermApp):
|
|
338
|
+
return _Unsupported(
|
|
339
|
+
f"a {term.functor}/{len(term.args)} term not in the measured corpus")
|
|
340
|
+
return _Unsupported(f"a bare atom {term!r} in referent position")
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _map_atom(cond: AceAtom, renamer: _Renamer,
|
|
344
|
+
be_event_dropped: frozenset) -> _Mapped:
|
|
345
|
+
"""One atomic condition → its kit shape, or a named refusal.
|
|
346
|
+
|
|
347
|
+
``be_event_dropped`` is the set of be-event referents (APE names) whose
|
|
348
|
+
equality translation drops them — computed once per DRS by the caller,
|
|
349
|
+
because whether ``be`` may become ``=`` depends on the WHOLE tree (see
|
|
350
|
+
the module docstring's copula paragraph).
|
|
351
|
+
"""
|
|
352
|
+
f, args = cond.functor, cond.args
|
|
353
|
+
|
|
354
|
+
if f == "object" and len(args) == 6:
|
|
355
|
+
ref, noun, cls, _unit, op, count = args
|
|
356
|
+
if not isinstance(ref, AceVar) or not isinstance(noun, str):
|
|
357
|
+
return _Unsupported("an object condition with a non-atomic noun")
|
|
358
|
+
if (op == "eq" and count == 1) or (cls == "mass" and op == "na"):
|
|
359
|
+
return ("pred", _kit_predicate(noun), (renamer.referent(ref),))
|
|
360
|
+
if op not in _ACE_OPS or not isinstance(count, int):
|
|
361
|
+
return _Unsupported(
|
|
362
|
+
f"a cardinality operator outside the measured corpus "
|
|
363
|
+
f"({op!r} {count!r})")
|
|
364
|
+
if noun == "na":
|
|
365
|
+
# A coordination group ("John and Mary"): its members arrive as
|
|
366
|
+
# has_part conditions; the object contributes ONLY |g| op n.
|
|
367
|
+
return ("card", renamer.referent(ref), _ACE_OPS[op], count)
|
|
368
|
+
# A counted plural ("at least 3 men"): |g| op n, every member a noun.
|
|
369
|
+
# ACE reads the unmarked plural COLLECTIVELY, so the group referent
|
|
370
|
+
# stays and the verb keeps applying to it — the box mapper expands
|
|
371
|
+
# this into Card plus a membership duplex.
|
|
372
|
+
return ("group_noun", renamer.referent(ref), _kit_predicate(noun),
|
|
373
|
+
_ACE_OPS[op], count)
|
|
374
|
+
|
|
375
|
+
if f == "predicate" and len(args) in (3, 4, 5):
|
|
376
|
+
event, verb = args[0], args[1]
|
|
377
|
+
if not isinstance(event, AceVar) or not isinstance(verb, str):
|
|
378
|
+
return _Unsupported("a predicate condition with a non-atomic verb")
|
|
379
|
+
if verb == "be" and len(args) == 4:
|
|
380
|
+
if event.name in be_event_dropped:
|
|
381
|
+
left = _term_name(args[2], renamer)
|
|
382
|
+
right = _term_name(args[3], renamer)
|
|
383
|
+
for side in (left, right):
|
|
384
|
+
if isinstance(side, _Unsupported):
|
|
385
|
+
return side
|
|
386
|
+
return ("eq", left, right)
|
|
387
|
+
# The be-event is itself talked about (modified) — keep it.
|
|
388
|
+
# Not measured in the corpus; the guard exists so that if APE
|
|
389
|
+
# ever produces it, the modifier keeps its subject instead of
|
|
390
|
+
# dangling.
|
|
391
|
+
participants: List[str] = [renamer.referent(event)]
|
|
392
|
+
for arg in args[2:]:
|
|
393
|
+
name = _term_name(arg, renamer)
|
|
394
|
+
if isinstance(name, _Unsupported):
|
|
395
|
+
return name
|
|
396
|
+
participants.append(name)
|
|
397
|
+
return ("pred", _kit_predicate(verb), tuple(participants))
|
|
398
|
+
|
|
399
|
+
if f == "property" and len(args) in (3, 4):
|
|
400
|
+
ref, adjective, degree = args[0], args[1], args[2]
|
|
401
|
+
if (not isinstance(ref, AceVar) or not isinstance(adjective, str)
|
|
402
|
+
or not isinstance(degree, str)):
|
|
403
|
+
return _Unsupported("a property condition outside the measured shapes")
|
|
404
|
+
names: List[str] = [renamer.referent(ref)]
|
|
405
|
+
for extra in args[3:]:
|
|
406
|
+
name = _term_name(extra, renamer)
|
|
407
|
+
if isinstance(name, _Unsupported):
|
|
408
|
+
return name
|
|
409
|
+
names.append(name)
|
|
410
|
+
return ("pred", _degree_name(adjective, degree), tuple(names))
|
|
411
|
+
|
|
412
|
+
if f == "relation" and len(args) == 3 and args[1] == "of":
|
|
413
|
+
left = _term_name(args[0], renamer)
|
|
414
|
+
right = _term_name(args[2], renamer)
|
|
415
|
+
for side in (left, right):
|
|
416
|
+
if isinstance(side, _Unsupported):
|
|
417
|
+
return side
|
|
418
|
+
return ("pred", "Of", (left, right))
|
|
419
|
+
|
|
420
|
+
if f == "modifier_adv" and len(args) == 3:
|
|
421
|
+
event, adverb, degree = args
|
|
422
|
+
if (not isinstance(event, AceVar) or not isinstance(adverb, str)
|
|
423
|
+
or not isinstance(degree, str)):
|
|
424
|
+
return _Unsupported("a modifier_adv outside the measured shapes")
|
|
425
|
+
return ("pred", _degree_name(adverb, degree),
|
|
426
|
+
(renamer.referent(event),))
|
|
427
|
+
|
|
428
|
+
if f == "modifier_pp" and len(args) == 3:
|
|
429
|
+
event, preposition, obj = args
|
|
430
|
+
if not isinstance(event, AceVar) or not isinstance(preposition, str):
|
|
431
|
+
return _Unsupported("a modifier_pp outside the measured shapes")
|
|
432
|
+
name = _term_name(obj, renamer)
|
|
433
|
+
if isinstance(name, _Unsupported):
|
|
434
|
+
return name
|
|
435
|
+
return ("pred", _kit_predicate(preposition),
|
|
436
|
+
(renamer.referent(event), name))
|
|
437
|
+
|
|
438
|
+
if f == "has_part" and len(args) == 2:
|
|
439
|
+
# APE argument order is (group, member); the kit condition reads
|
|
440
|
+
# Part(member, group).
|
|
441
|
+
group, member = args
|
|
442
|
+
if not isinstance(group, AceVar):
|
|
443
|
+
return _Unsupported("a has_part whose group is not a referent")
|
|
444
|
+
member_name = _term_name(member, renamer)
|
|
445
|
+
if isinstance(member_name, _Unsupported):
|
|
446
|
+
return member_name
|
|
447
|
+
return ("part", member_name, renamer.referent(group))
|
|
448
|
+
if f == "query":
|
|
449
|
+
return _Unsupported(
|
|
450
|
+
"an interrogative marker: a question is not an assertion — "
|
|
451
|
+
"ace_to_formula carries it", "ACE-3")
|
|
452
|
+
if f == "formula":
|
|
453
|
+
return _Unsupported(
|
|
454
|
+
"arithmetic terms have no place in a DRS condition's "
|
|
455
|
+
"referent/constant arguments -- ace_to_formula carries it",
|
|
456
|
+
"ACE-4")
|
|
457
|
+
return _Unsupported(
|
|
458
|
+
f"{f}/{len(args)} is not in the measured corpus")
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _be_events_to_drop(root: AceDrs) -> frozenset:
|
|
462
|
+
"""Referents of ``be`` events that occur NOWHERE besides their own
|
|
463
|
+
condition — exactly those may be translated away into an equality."""
|
|
464
|
+
occurrences: Dict[str, int] = {}
|
|
465
|
+
be_events: List[str] = []
|
|
466
|
+
for cond in root.walk_conditions():
|
|
467
|
+
if not isinstance(cond, AceAtom):
|
|
468
|
+
continue
|
|
469
|
+
if (cond.functor == "predicate" and len(cond.args) == 4
|
|
470
|
+
and cond.args[1] == "be" and isinstance(cond.args[0], AceVar)):
|
|
471
|
+
be_events.append(cond.args[0].name)
|
|
472
|
+
for arg in cond.args:
|
|
473
|
+
if isinstance(arg, AceVar):
|
|
474
|
+
occurrences[arg.name] = occurrences.get(arg.name, 0) + 1
|
|
475
|
+
return frozenset(e for e in be_events if occurrences.get(e, 0) == 1)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
# ---------------------------------------------------------------------------
|
|
479
|
+
# Box mapping
|
|
480
|
+
# ---------------------------------------------------------------------------
|
|
481
|
+
|
|
482
|
+
def _shell_render(cond: AceCondition) -> str:
|
|
483
|
+
"""A complex condition's row rendering: functor with boxes elided —
|
|
484
|
+
the boxes' own conditions get rows of their own."""
|
|
485
|
+
if isinstance(cond, AceImpl):
|
|
486
|
+
return "=>(…)"
|
|
487
|
+
if isinstance(cond, AceOr):
|
|
488
|
+
return "v(…)"
|
|
489
|
+
if isinstance(cond, AceNeg):
|
|
490
|
+
return "-(…)"
|
|
491
|
+
if isinstance(cond, AceNaf):
|
|
492
|
+
return "~(…)"
|
|
493
|
+
if isinstance(cond, AceModal):
|
|
494
|
+
return f"{cond.modality}(…)"
|
|
495
|
+
if isinstance(cond, AceQuestion):
|
|
496
|
+
return "question(…)"
|
|
497
|
+
if isinstance(cond, AceCommand):
|
|
498
|
+
return "command(…)"
|
|
499
|
+
return "[…]"
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
class _BoxMapper:
|
|
503
|
+
def __init__(self, root: AceDrs):
|
|
504
|
+
self.renamer = _Renamer(root)
|
|
505
|
+
self.dropped = _be_events_to_drop(root)
|
|
506
|
+
self.rows: List[ConditionReport] = []
|
|
507
|
+
self.complete = True
|
|
508
|
+
|
|
509
|
+
def map_box(self, box: AceDrs) -> Optional[DRS]:
|
|
510
|
+
referents = tuple(
|
|
511
|
+
self.renamer.referent(r) for r in box.referents
|
|
512
|
+
if r.name not in self.dropped)
|
|
513
|
+
conditions: List[Condition] = []
|
|
514
|
+
for cond in box.conditions:
|
|
515
|
+
conditions.extend(self.map_condition(cond))
|
|
516
|
+
if not self.complete:
|
|
517
|
+
return None
|
|
518
|
+
return DRS(referents, tuple(conditions))
|
|
519
|
+
|
|
520
|
+
def map_condition(self, cond: AceCondition) -> List[Condition]:
|
|
521
|
+
if isinstance(cond, AceAtom):
|
|
522
|
+
result = _map_atom(cond, self.renamer, self.dropped)
|
|
523
|
+
if isinstance(result, _Unsupported):
|
|
524
|
+
self.complete = False
|
|
525
|
+
self.rows.append(ConditionReport(
|
|
526
|
+
cond.render(), "unsupported", reason=result.reason,
|
|
527
|
+
milestone=result.milestone, sentence=cond.sentence,
|
|
528
|
+
token=cond.token))
|
|
529
|
+
return []
|
|
530
|
+
targets = self._kit_conditions(result)
|
|
531
|
+
self.rows.append(ConditionReport(
|
|
532
|
+
cond.render(), "mapped",
|
|
533
|
+
target=", ".join(c.to_box_notation() for c in targets),
|
|
534
|
+
sentence=cond.sentence, token=cond.token))
|
|
535
|
+
return targets
|
|
536
|
+
if isinstance(cond, AceNeg):
|
|
537
|
+
self.rows.append(ConditionReport("-(…)", "mapped", target="~[…]"))
|
|
538
|
+
sub = self.map_box(cond.drs)
|
|
539
|
+
return [Neg(sub)] if sub is not None else []
|
|
540
|
+
if isinstance(cond, AceImpl):
|
|
541
|
+
self.rows.append(ConditionReport("=>(…)", "mapped",
|
|
542
|
+
target="[…] -> […]"))
|
|
543
|
+
antecedent = self.map_box(cond.antecedent)
|
|
544
|
+
consequent = self.map_box(cond.consequent)
|
|
545
|
+
if antecedent is None or consequent is None:
|
|
546
|
+
return []
|
|
547
|
+
return [Impl(antecedent, consequent)]
|
|
548
|
+
if isinstance(cond, AceOr):
|
|
549
|
+
self.rows.append(ConditionReport("v(…)", "mapped",
|
|
550
|
+
target="[…] ∨ […]"))
|
|
551
|
+
left = self.map_box(cond.left)
|
|
552
|
+
right = self.map_box(cond.right)
|
|
553
|
+
if left is None or right is None:
|
|
554
|
+
return []
|
|
555
|
+
return [Or(left, right)]
|
|
556
|
+
# Everything below is a named refusal at THIS route; sub-box
|
|
557
|
+
# conditions still get their own rows so the report stays complete.
|
|
558
|
+
self.complete = False
|
|
559
|
+
reason, milestone = _complex_refusal(cond)
|
|
560
|
+
self.rows.append(ConditionReport(
|
|
561
|
+
_shell_render(cond), "unsupported", reason=reason,
|
|
562
|
+
milestone=milestone))
|
|
563
|
+
for sub in _iter_sub_boxes(cond):
|
|
564
|
+
self.map_box(sub)
|
|
565
|
+
return []
|
|
566
|
+
|
|
567
|
+
def _kit_conditions(self, result) -> List[Condition]:
|
|
568
|
+
"""One mapped atom shape -> its kit condition(s). The plural object
|
|
569
|
+
is the one shape that expands to TWO: ``Card`` plus the membership
|
|
570
|
+
duplex distributing the noun over the parts (the group itself stays
|
|
571
|
+
a term for the verb -- ACE reads the unmarked plural collectively)."""
|
|
572
|
+
from ..drt.nodes import Card, Part
|
|
573
|
+
|
|
574
|
+
kind = result[0]
|
|
575
|
+
if kind == "eq":
|
|
576
|
+
return [Eq(result[1], result[2])]
|
|
577
|
+
if kind == "pred":
|
|
578
|
+
return [Pred(result[1], result[2])]
|
|
579
|
+
if kind == "card":
|
|
580
|
+
return [Card(result[1], result[2], result[3])]
|
|
581
|
+
if kind == "part":
|
|
582
|
+
return [Part(result[1], result[2])]
|
|
583
|
+
if kind == "group_noun":
|
|
584
|
+
_, group, noun, op, count = result
|
|
585
|
+
member = self.renamer.fresh("x")
|
|
586
|
+
membership = Impl(DRS((member,), (Part(member, group),)),
|
|
587
|
+
DRS((), (Pred(noun, (member,)),)))
|
|
588
|
+
return [Card(group, op, count), membership]
|
|
589
|
+
raise AssertionError(f"unknown mapped shape {kind!r}")
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _complex_refusal(cond: AceCondition) -> Tuple[str, str]:
|
|
593
|
+
if isinstance(cond, AceModal):
|
|
594
|
+
return (f"a modal box ({cond.modality}): no modal conditions in a "
|
|
595
|
+
"classical DRS — ace_to_formula carries it", "ACE-3")
|
|
596
|
+
if isinstance(cond, AceQuestion):
|
|
597
|
+
return ("a question is not an assertion — ace_to_formula carries it",
|
|
598
|
+
"ACE-3")
|
|
599
|
+
if isinstance(cond, AceCommand):
|
|
600
|
+
return ("an imperative: no command semantics decided", "")
|
|
601
|
+
if isinstance(cond, AceNaf):
|
|
602
|
+
return ("negation as failure is not classical negation; the "
|
|
603
|
+
"non-monotonic route is undecided", "")
|
|
604
|
+
if isinstance(cond, AceCondList):
|
|
605
|
+
return ("the maximality reading of exactly/at most needs a counting "
|
|
606
|
+
"quantifier over a compound scope, which is a FORMULA-level "
|
|
607
|
+
"construct -- ace_to_formula carries it", "ACE-4")
|
|
608
|
+
return ("not in the measured corpus", "")
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def _iter_sub_boxes(cond: AceCondition):
|
|
612
|
+
from .drs_reader import _sub_boxes
|
|
613
|
+
yield from _sub_boxes(cond)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def map_ace_drs(ace_drs: AceDrs) -> DrsMapping:
|
|
617
|
+
"""APE's DRS → :class:`DrsMapping` (kit DRS iff complete, report always).
|
|
618
|
+
|
|
619
|
+
The returned kit DRS has passed :meth:`unicode_logic_kit.drt.nodes.DRS
|
|
620
|
+
.validate` — the renamer assigns globally unique referent names, which
|
|
621
|
+
is exactly the invariant the kit core demands.
|
|
622
|
+
"""
|
|
623
|
+
mapper = _BoxMapper(ace_drs)
|
|
624
|
+
drs = mapper.map_box(ace_drs)
|
|
625
|
+
if drs is not None:
|
|
626
|
+
drs.validate()
|
|
627
|
+
return DrsMapping(drs=drs, rows=tuple(mapper.rows),
|
|
628
|
+
referents=dict(mapper.renamer.referents),
|
|
629
|
+
constants=dict(mapper.renamer.constants))
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def ace_to_drs(text: str, *, ulex: Optional[str] = None, guess: bool = False,
|
|
633
|
+
timeout: float = 30.0) -> DRS:
|
|
634
|
+
"""ACE text → kit DRS, via APE and :func:`map_ace_drs`.
|
|
635
|
+
|
|
636
|
+
The whole text becomes ONE box (APE merges sentences and resolves
|
|
637
|
+
cross-sentence anaphora before this module ever sees the DRS). Feed the
|
|
638
|
+
result to :func:`unicode_logic_kit.drt.export.drs_to_fol` for FOL, or use
|
|
639
|
+
:func:`unicode_logic_kit.ace.translate.ace_to_formula` directly when the
|
|
640
|
+
text may contain modality or questions.
|
|
641
|
+
|
|
642
|
+
Raises:
|
|
643
|
+
unicode_logic_kit.ace.runner.AceParseError: the text is not ACE.
|
|
644
|
+
unicode_logic_kit.ace.mapping.AceUnsupportedError: a condition is
|
|
645
|
+
outside the classical DRS core; ``rows`` names every condition
|
|
646
|
+
and the refusals' milestones.
|
|
647
|
+
unicode_logic_kit.ace.runner.ApeUnavailableError: no APE binary
|
|
648
|
+
reachable.
|
|
649
|
+
"""
|
|
650
|
+
result = run_ape(text, ulex=ulex, guess=guess, timeout=timeout)
|
|
651
|
+
if not result.accepted:
|
|
652
|
+
first = result.error_messages[0]
|
|
653
|
+
raise AceParseError(
|
|
654
|
+
f"not ACE: {first.value!r}"
|
|
655
|
+
+ (f" (repair hint: {first.repair!r})" if first.repair else ""),
|
|
656
|
+
result.error_messages)
|
|
657
|
+
mapping = map_ace_drs(parse_ape_drs(result.drs))
|
|
658
|
+
if not mapping.complete:
|
|
659
|
+
problems = "; ".join(
|
|
660
|
+
f"{r.condition}: {r.reason}"
|
|
661
|
+
+ (f" [{r.milestone}]" if r.milestone else "")
|
|
662
|
+
for r in mapping.unsupported)
|
|
663
|
+
raise AceUnsupportedError(
|
|
664
|
+
f"the classical DRS core cannot carry this text — {problems}",
|
|
665
|
+
mapping.rows)
|
|
666
|
+
return mapping.drs
|