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,570 @@
|
|
|
1
|
+
"""Read APE's DRS term into a faithful 1:1 object model — no interpretation.
|
|
2
|
+
|
|
3
|
+
APE prints its Discourse Representation Structure as one Prolog term::
|
|
4
|
+
|
|
5
|
+
drs([A,B],[object(A,man,countable,na,eq,1)-1/2,predicate(B,wait,A)-1/3])
|
|
6
|
+
|
|
7
|
+
This module parses exactly that surface into frozen dataclasses and does
|
|
8
|
+
NOTHING else: no renaming, no dropping, no semantic choices. Interpretation —
|
|
9
|
+
which conditions the kit can carry, and as what — is
|
|
10
|
+
:mod:`unicode_logic_kit.ace.mapping`'s job, and keeping the two apart is what
|
|
11
|
+
makes the mapping's per-condition coverage report trustworthy: every condition
|
|
12
|
+
APE emitted is HERE, so a condition the report does not mention cannot exist.
|
|
13
|
+
|
|
14
|
+
The shape inventory below is measured, not copied from documentation — every
|
|
15
|
+
form appears in ``tests/fixtures/ape_5f4d535_corpus_v1.json``, recorded from
|
|
16
|
+
APE at the pinned commit over the kit's own 55-sentence corpus:
|
|
17
|
+
|
|
18
|
+
- atomic conditions ``functor(args)-S/T`` with a sentence/token index
|
|
19
|
+
(``-1/2``; implicit conditions carry ``-1/''`` — an EMPTY token). Functors
|
|
20
|
+
seen: ``object/6``, ``predicate/3..5``, ``property/3..4``, ``relation/3``,
|
|
21
|
+
``modifier_adv/3``, ``modifier_pp/3``, ``has_part/2``, ``query/2``,
|
|
22
|
+
``formula/3``.
|
|
23
|
+
- complex conditions, none of which carry an index: ``-(drs)`` negation,
|
|
24
|
+
``~(drs)`` negation as failure, ``=>(drs,drs)`` the duplex condition,
|
|
25
|
+
``v(drs,drs)`` disjunction, the four modal boxes ``must/can/should/may
|
|
26
|
+
(drs)``, ``question(drs)``, ``command(drs)``.
|
|
27
|
+
- a plain Prolog LIST of conditions as itself a condition — APE's grouping
|
|
28
|
+
for ``exactly``/``at most`` cardinalities (the group carries the maximality
|
|
29
|
+
reading jointly, which is why it is not just conjunction and why
|
|
30
|
+
:mod:`.mapping` refuses it until the Count route exists).
|
|
31
|
+
- terms: variables (``A``), lowercase atoms (``man``, ``eq``, ``na``,
|
|
32
|
+
``who``, ``comp_than``), integers (bare, as in ``eq,1``), and the wrapped
|
|
33
|
+
values ``named('John')``, ``int(30)``, ``real(...)``, ``string('Johnny')``,
|
|
34
|
+
``expr(+,int(1),int(2))`` (arithmetic, nesting).
|
|
35
|
+
|
|
36
|
+
The parser is a hand-written tokenizer + recursive descent over THIS
|
|
37
|
+
inventory, generic enough that an unseen functor still parses (into
|
|
38
|
+
:class:`AceAtom` / :class:`AceTermApp`) rather than crashing — it becomes the
|
|
39
|
+
mapping layer's "not in the measured corpus" report row instead. A text that
|
|
40
|
+
is not even a well-formed term (truncated output, a changed APE) raises
|
|
41
|
+
:class:`AceDrsUnreadError` with position and context.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
import re
|
|
47
|
+
from dataclasses import dataclass
|
|
48
|
+
from typing import List, Optional, Tuple, Union
|
|
49
|
+
|
|
50
|
+
from ..fol._fol_nodes import _numeral_from_text
|
|
51
|
+
from .runner import AceError
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"parse_ape_drs", "AceDrsUnreadError",
|
|
55
|
+
"AceDrs", "AceVar", "AceNamed", "AceInt", "AceReal", "AceString",
|
|
56
|
+
"AceExpr", "AceTermApp",
|
|
57
|
+
"AceAtom", "AceNeg", "AceNaf", "AceImpl", "AceOr", "AceModal",
|
|
58
|
+
"AceQuestion", "AceCommand", "AceCondList",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AceDrsUnreadError(AceError):
|
|
63
|
+
"""APE's DRS text is not a term this reader understands — either not a
|
|
64
|
+
well-formed Prolog term at all, or a structure outside every shape in
|
|
65
|
+
the measured inventory (see the module docstring)."""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Terms
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class AceVar:
|
|
74
|
+
"""A discourse referent as APE names it (``A``, ``B``, …). Kept verbatim;
|
|
75
|
+
the kit-side renaming is the mapping layer's concern."""
|
|
76
|
+
|
|
77
|
+
name: str
|
|
78
|
+
|
|
79
|
+
def render(self) -> str:
|
|
80
|
+
return self.name
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True)
|
|
84
|
+
class AceNamed:
|
|
85
|
+
"""``named('John')`` — a proper name."""
|
|
86
|
+
|
|
87
|
+
name: str
|
|
88
|
+
|
|
89
|
+
def render(self) -> str:
|
|
90
|
+
return f"named('{self.name}')"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class AceInt:
|
|
95
|
+
value: int
|
|
96
|
+
|
|
97
|
+
def render(self) -> str:
|
|
98
|
+
return f"int({self.value})"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(frozen=True)
|
|
102
|
+
class AceReal:
|
|
103
|
+
"""``real(3.14)`` — a decimal value, read exactly or refused.
|
|
104
|
+
|
|
105
|
+
A ``float`` when a float holds the numeral exactly, the ``int`` itself when only an integer
|
|
106
|
+
does (``real(100000000000000000000000.0)`` is ``10**23``, which no float is). A decimal of
|
|
107
|
+
more than 15 significant digits is refused by the reader with :class:`AceDrsUnreadError`: two
|
|
108
|
+
different decimals of that length can be one float, and a numeral is identified by its value.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
value: Union[int, float]
|
|
112
|
+
|
|
113
|
+
def render(self) -> str:
|
|
114
|
+
return f"real({self.value})"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass(frozen=True)
|
|
118
|
+
class AceString:
|
|
119
|
+
value: str
|
|
120
|
+
|
|
121
|
+
def render(self) -> str:
|
|
122
|
+
return f"string('{self.value}')"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True)
|
|
126
|
+
class AceExpr:
|
|
127
|
+
"""``expr(+, int(1), int(2))`` — an arithmetic expression term."""
|
|
128
|
+
|
|
129
|
+
op: str
|
|
130
|
+
left: "AceTerm"
|
|
131
|
+
right: "AceTerm"
|
|
132
|
+
|
|
133
|
+
def render(self) -> str:
|
|
134
|
+
return f"expr({self.op},{_render(self.left)},{_render(self.right)})"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass(frozen=True)
|
|
138
|
+
class AceTermApp:
|
|
139
|
+
"""A functored term outside the known wrappers — kept verbatim so an APE
|
|
140
|
+
novelty surfaces in the coverage report instead of crashing the reader."""
|
|
141
|
+
|
|
142
|
+
functor: str
|
|
143
|
+
args: Tuple["AceTerm", ...]
|
|
144
|
+
|
|
145
|
+
def render(self) -> str:
|
|
146
|
+
return f"{self.functor}({','.join(_render(a) for a in self.args)})"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
#: A term: a referent, a bare atom (str), or one of the wrapped values.
|
|
150
|
+
AceTerm = Union[AceVar, str, int, AceNamed, AceInt, AceReal, AceString,
|
|
151
|
+
AceExpr, AceTermApp]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _render(term: AceTerm) -> str:
|
|
155
|
+
if isinstance(term, str):
|
|
156
|
+
return term
|
|
157
|
+
if isinstance(term, int):
|
|
158
|
+
return str(term)
|
|
159
|
+
return term.render()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# ---------------------------------------------------------------------------
|
|
163
|
+
# Conditions
|
|
164
|
+
# ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
@dataclass(frozen=True)
|
|
167
|
+
class AceAtom:
|
|
168
|
+
"""An atomic condition: ``functor(args)`` plus its sentence/token index.
|
|
169
|
+
|
|
170
|
+
``token`` is ``None`` for implicit conditions (APE prints ``-1/''`` for
|
|
171
|
+
e.g. the group-membership ``has_part`` facts a coordination introduces).
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
functor: str
|
|
175
|
+
args: Tuple[AceTerm, ...]
|
|
176
|
+
sentence: Optional[int] = None
|
|
177
|
+
token: Optional[int] = None
|
|
178
|
+
|
|
179
|
+
def render(self) -> str:
|
|
180
|
+
body = f"{self.functor}({','.join(_render(a) for a in self.args)})"
|
|
181
|
+
if self.sentence is None:
|
|
182
|
+
return body
|
|
183
|
+
tok = self.token if self.token is not None else "''"
|
|
184
|
+
return f"{body}-{self.sentence}/{tok}"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass(frozen=True)
|
|
188
|
+
class AceNeg:
|
|
189
|
+
"""``-(drs)`` — classical negation of a sub-box."""
|
|
190
|
+
|
|
191
|
+
drs: "AceDrs"
|
|
192
|
+
|
|
193
|
+
def render(self) -> str:
|
|
194
|
+
return f"-({self.drs.render()})"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass(frozen=True)
|
|
198
|
+
class AceNaf:
|
|
199
|
+
"""``~(drs)`` — negation as failure ("it is not provable that")."""
|
|
200
|
+
|
|
201
|
+
drs: "AceDrs"
|
|
202
|
+
|
|
203
|
+
def render(self) -> str:
|
|
204
|
+
return f"~({self.drs.render()})"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@dataclass(frozen=True)
|
|
208
|
+
class AceImpl:
|
|
209
|
+
"""``=>(drs,drs)`` — the duplex condition (every/no/if-then/each-of)."""
|
|
210
|
+
|
|
211
|
+
antecedent: "AceDrs"
|
|
212
|
+
consequent: "AceDrs"
|
|
213
|
+
|
|
214
|
+
def render(self) -> str:
|
|
215
|
+
return f"=>({self.antecedent.render()},{self.consequent.render()})"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass(frozen=True)
|
|
219
|
+
class AceOr:
|
|
220
|
+
"""``v(drs,drs)`` — disjunction of two sub-boxes."""
|
|
221
|
+
|
|
222
|
+
left: "AceDrs"
|
|
223
|
+
right: "AceDrs"
|
|
224
|
+
|
|
225
|
+
def render(self) -> str:
|
|
226
|
+
return f"v({self.left.render()},{self.right.render()})"
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@dataclass(frozen=True)
|
|
230
|
+
class AceModal:
|
|
231
|
+
"""``must/can/should/may (drs)`` — one of ACE's four modal boxes."""
|
|
232
|
+
|
|
233
|
+
modality: str
|
|
234
|
+
drs: "AceDrs"
|
|
235
|
+
|
|
236
|
+
def render(self) -> str:
|
|
237
|
+
return f"{self.modality}({self.drs.render()})"
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@dataclass(frozen=True)
|
|
241
|
+
class AceQuestion:
|
|
242
|
+
"""``question(drs)`` — an interrogative sentence's box."""
|
|
243
|
+
|
|
244
|
+
drs: "AceDrs"
|
|
245
|
+
|
|
246
|
+
def render(self) -> str:
|
|
247
|
+
return f"question({self.drs.render()})"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@dataclass(frozen=True)
|
|
251
|
+
class AceCommand:
|
|
252
|
+
"""``command(drs)`` — an imperative sentence's box."""
|
|
253
|
+
|
|
254
|
+
drs: "AceDrs"
|
|
255
|
+
|
|
256
|
+
def render(self) -> str:
|
|
257
|
+
return f"command({self.drs.render()})"
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@dataclass(frozen=True)
|
|
261
|
+
class AceCondList:
|
|
262
|
+
"""A LIST of conditions as itself a condition — APE's joint grouping for
|
|
263
|
+
``exactly``/``at most`` cardinalities (see the module docstring)."""
|
|
264
|
+
|
|
265
|
+
conditions: Tuple["AceCondition", ...]
|
|
266
|
+
|
|
267
|
+
def render(self) -> str:
|
|
268
|
+
return "[" + ",".join(c.render() for c in self.conditions) + "]"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
AceCondition = Union[AceAtom, AceNeg, AceNaf, AceImpl, AceOr, AceModal,
|
|
272
|
+
AceQuestion, AceCommand, AceCondList]
|
|
273
|
+
|
|
274
|
+
#: Complex-condition functors, mapped to their dataclass. ``v`` is a plain
|
|
275
|
+
#: atom name in Prolog, so classification happens AFTER a generic parse: an
|
|
276
|
+
#: application is complex iff its functor is listed here AND every argument
|
|
277
|
+
#: is a DRS — ``v(A,B)`` with referent args would stay an ordinary AceAtom.
|
|
278
|
+
_COMPLEX = {
|
|
279
|
+
"-": (AceNeg, 1), "~": (AceNaf, 1), "=>": (AceImpl, 2), "v": (AceOr, 2),
|
|
280
|
+
"question": (AceQuestion, 1), "command": (AceCommand, 1),
|
|
281
|
+
"must": (AceModal, 1), "can": (AceModal, 1), "should": (AceModal, 1),
|
|
282
|
+
"may": (AceModal, 1),
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@dataclass(frozen=True)
|
|
287
|
+
class AceDrs:
|
|
288
|
+
"""One box: declared referents plus conditions, verbatim from APE."""
|
|
289
|
+
|
|
290
|
+
referents: Tuple[AceVar, ...]
|
|
291
|
+
conditions: Tuple[AceCondition, ...]
|
|
292
|
+
|
|
293
|
+
def render(self) -> str:
|
|
294
|
+
refs = ",".join(v.name for v in self.referents)
|
|
295
|
+
conds = ",".join(c.render() for c in self.conditions)
|
|
296
|
+
return f"drs([{refs}],[{conds}])"
|
|
297
|
+
|
|
298
|
+
def walk_conditions(self):
|
|
299
|
+
"""Every condition in the tree, pre-order — sub-boxes AND the inner
|
|
300
|
+
conditions of a list condition included (a list's members are
|
|
301
|
+
conditions in their own right; skipping them made the renamer's
|
|
302
|
+
event detection blind to ``exactly``/``at most`` scopes)."""
|
|
303
|
+
for cond in self.conditions:
|
|
304
|
+
yield from _walk_condition(cond)
|
|
305
|
+
|
|
306
|
+
def walk_boxes(self):
|
|
307
|
+
"""Every box in the tree, pre-order, this one first."""
|
|
308
|
+
yield self
|
|
309
|
+
for cond in self.conditions:
|
|
310
|
+
for sub in _sub_boxes(cond):
|
|
311
|
+
yield from sub.walk_boxes()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _walk_condition(cond: "AceCondition"):
|
|
315
|
+
yield cond
|
|
316
|
+
if isinstance(cond, AceCondList):
|
|
317
|
+
for inner in cond.conditions:
|
|
318
|
+
yield from _walk_condition(inner)
|
|
319
|
+
else:
|
|
320
|
+
for sub in _sub_boxes(cond):
|
|
321
|
+
yield from sub.walk_conditions()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _sub_boxes(cond: AceCondition):
|
|
325
|
+
if isinstance(cond, (AceNeg, AceNaf, AceModal, AceQuestion, AceCommand)):
|
|
326
|
+
yield cond.drs
|
|
327
|
+
elif isinstance(cond, AceImpl):
|
|
328
|
+
yield cond.antecedent
|
|
329
|
+
yield cond.consequent
|
|
330
|
+
elif isinstance(cond, AceOr):
|
|
331
|
+
yield cond.left
|
|
332
|
+
yield cond.right
|
|
333
|
+
elif isinstance(cond, AceCondList):
|
|
334
|
+
for inner in cond.conditions:
|
|
335
|
+
yield from _sub_boxes(inner)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
# ---------------------------------------------------------------------------
|
|
339
|
+
# Tokenizer + recursive descent
|
|
340
|
+
# ---------------------------------------------------------------------------
|
|
341
|
+
|
|
342
|
+
_TOKEN = re.compile(r"""
|
|
343
|
+
(?P<ws>\s+)
|
|
344
|
+
| (?P<real>\d+\.\d+)
|
|
345
|
+
| (?P<int>\d+)
|
|
346
|
+
| (?P<var>[A-Z_][A-Za-z0-9_]*)
|
|
347
|
+
| (?P<atom>[a-z][A-Za-z0-9_]*)
|
|
348
|
+
| (?P<quoted>'(?:[^']|'')*')
|
|
349
|
+
| (?P<symbol>=>|\\/|[-~=+*/^<>])
|
|
350
|
+
| (?P<punct>[()\[\],|])
|
|
351
|
+
""", re.VERBOSE)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _tokenize(text: str) -> List[Tuple[str, str, int]]:
|
|
355
|
+
tokens = []
|
|
356
|
+
pos = 0
|
|
357
|
+
while pos < len(text):
|
|
358
|
+
match = _TOKEN.match(text, pos)
|
|
359
|
+
if match is None:
|
|
360
|
+
raise AceDrsUnreadError(
|
|
361
|
+
f"DRS reader: unexpected character {text[pos]!r} at offset "
|
|
362
|
+
f"{pos} in {text[max(0, pos - 30):pos + 30]!r}")
|
|
363
|
+
kind = match.lastgroup
|
|
364
|
+
if kind != "ws":
|
|
365
|
+
tokens.append((kind, match.group(), pos))
|
|
366
|
+
pos = match.end()
|
|
367
|
+
tokens.append(("end", "", pos))
|
|
368
|
+
return tokens
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class _Parser:
|
|
372
|
+
def __init__(self, text: str):
|
|
373
|
+
self.text = text
|
|
374
|
+
self.tokens = _tokenize(text)
|
|
375
|
+
self.i = 0
|
|
376
|
+
|
|
377
|
+
# -- token plumbing ----------------------------------------------------
|
|
378
|
+
def peek(self, ahead: int = 0) -> Tuple[str, str]:
|
|
379
|
+
kind, value, _ = self.tokens[min(self.i + ahead, len(self.tokens) - 1)]
|
|
380
|
+
return kind, value
|
|
381
|
+
|
|
382
|
+
def take(self, kind: Optional[str] = None, value: Optional[str] = None):
|
|
383
|
+
actual_kind, actual_value, pos = self.tokens[self.i]
|
|
384
|
+
if ((kind is not None and actual_kind != kind)
|
|
385
|
+
or (value is not None and actual_value != value)):
|
|
386
|
+
want = value if value is not None else kind
|
|
387
|
+
raise AceDrsUnreadError(
|
|
388
|
+
f"DRS reader: expected {want!r}, found {actual_value!r} at "
|
|
389
|
+
f"offset {pos} in {self.text[max(0, pos - 30):pos + 30]!r}")
|
|
390
|
+
self.i += 1
|
|
391
|
+
return actual_value
|
|
392
|
+
|
|
393
|
+
# -- grammar -----------------------------------------------------------
|
|
394
|
+
def parse(self) -> AceDrs:
|
|
395
|
+
drs = self.drs()
|
|
396
|
+
self.take("end")
|
|
397
|
+
return drs
|
|
398
|
+
|
|
399
|
+
def drs(self) -> AceDrs:
|
|
400
|
+
self.take("atom", "drs")
|
|
401
|
+
self.take("punct", "(")
|
|
402
|
+
self.take("punct", "[")
|
|
403
|
+
referents: List[AceVar] = []
|
|
404
|
+
while self.peek() != ("punct", "]"):
|
|
405
|
+
referents.append(AceVar(self.take("var")))
|
|
406
|
+
if self.peek() == ("punct", ","):
|
|
407
|
+
self.take()
|
|
408
|
+
self.take("punct", "]")
|
|
409
|
+
self.take("punct", ",")
|
|
410
|
+
conditions = self.condition_list()
|
|
411
|
+
self.take("punct", ")")
|
|
412
|
+
return AceDrs(tuple(referents), tuple(conditions))
|
|
413
|
+
|
|
414
|
+
def condition_list(self) -> List[AceCondition]:
|
|
415
|
+
self.take("punct", "[")
|
|
416
|
+
conditions: List[AceCondition] = []
|
|
417
|
+
while self.peek() != ("punct", "]"):
|
|
418
|
+
conditions.append(self.condition())
|
|
419
|
+
if self.peek() == ("punct", ","):
|
|
420
|
+
self.take()
|
|
421
|
+
self.take("punct", "]")
|
|
422
|
+
return conditions
|
|
423
|
+
|
|
424
|
+
def condition(self) -> AceCondition:
|
|
425
|
+
kind, value = self.peek()
|
|
426
|
+
if (kind, value) == ("punct", "["):
|
|
427
|
+
return AceCondList(tuple(self.condition_list()))
|
|
428
|
+
if kind in ("atom", "symbol") and self.peek(1) == ("punct", "("):
|
|
429
|
+
functor = self.take()
|
|
430
|
+
args = self.application_args()
|
|
431
|
+
complex_entry = _COMPLEX.get(functor)
|
|
432
|
+
if (complex_entry is not None
|
|
433
|
+
and len(args) == complex_entry[1]
|
|
434
|
+
and all(isinstance(a, AceDrs) for a in args)):
|
|
435
|
+
cls = complex_entry[0]
|
|
436
|
+
if cls is AceModal:
|
|
437
|
+
return AceModal(functor, *args)
|
|
438
|
+
return cls(*args)
|
|
439
|
+
atom_args = tuple(a for a in args)
|
|
440
|
+
sentence, token = self.optional_index()
|
|
441
|
+
return AceAtom(functor, atom_args, sentence, token)
|
|
442
|
+
raise AceDrsUnreadError(
|
|
443
|
+
f"DRS reader: a condition cannot start with {value!r} "
|
|
444
|
+
f"(offset {self.tokens[self.i][2]})")
|
|
445
|
+
|
|
446
|
+
def application_args(self) -> List:
|
|
447
|
+
"""The argument list of ``functor(...)`` — each argument is a term,
|
|
448
|
+
or a whole sub-``drs`` for the complex conditions."""
|
|
449
|
+
self.take("punct", "(")
|
|
450
|
+
args: List = []
|
|
451
|
+
while self.peek() != ("punct", ")"):
|
|
452
|
+
if self.peek() == ("atom", "drs") and self.peek(1) == ("punct", "("):
|
|
453
|
+
args.append(self.drs())
|
|
454
|
+
else:
|
|
455
|
+
args.append(self.term())
|
|
456
|
+
if self.peek() == ("punct", ","):
|
|
457
|
+
self.take()
|
|
458
|
+
self.take("punct", ")")
|
|
459
|
+
return args
|
|
460
|
+
|
|
461
|
+
def optional_index(self) -> Tuple[Optional[int], Optional[int]]:
|
|
462
|
+
"""``-S/T`` after an atomic condition; ``T`` may be ``''``."""
|
|
463
|
+
if self.peek() != ("symbol", "-"):
|
|
464
|
+
return None, None
|
|
465
|
+
kind, _ = self.peek(1)
|
|
466
|
+
if kind != "int":
|
|
467
|
+
return None, None
|
|
468
|
+
self.take("symbol", "-")
|
|
469
|
+
sentence = int(self.take("int"))
|
|
470
|
+
self.take("symbol", "/")
|
|
471
|
+
kind, _ = self.peek()
|
|
472
|
+
if kind == "int":
|
|
473
|
+
return sentence, int(self.take("int"))
|
|
474
|
+
quoted = self.take("quoted")
|
|
475
|
+
if quoted != "''":
|
|
476
|
+
raise AceDrsUnreadError(
|
|
477
|
+
f"DRS reader: expected an integer or '' as token index, "
|
|
478
|
+
f"found {quoted!r}")
|
|
479
|
+
return sentence, None
|
|
480
|
+
|
|
481
|
+
def real(self, text: str) -> Union[int, float]:
|
|
482
|
+
"""The value of the decimal ``text``, read exactly or refused by name.
|
|
483
|
+
|
|
484
|
+
The rule of every numeral reader of the kit: a whole value is an ``int`` of its digits, any
|
|
485
|
+
other decimal is the float it spells when it has at most 15 significant digits, and a
|
|
486
|
+
longer one is refused, because two different decimals of that length can be one float and
|
|
487
|
+
a numeral is identified by its value.
|
|
488
|
+
|
|
489
|
+
Raises:
|
|
490
|
+
AceDrsUnreadError: the text is a decimal that no float holds exactly.
|
|
491
|
+
"""
|
|
492
|
+
try:
|
|
493
|
+
return _numeral_from_text(text)
|
|
494
|
+
except ValueError as exc:
|
|
495
|
+
raise AceDrsUnreadError(f"DRS reader: the real {text} is not read: {exc}") from None
|
|
496
|
+
|
|
497
|
+
def term(self) -> AceTerm:
|
|
498
|
+
kind, value = self.peek()
|
|
499
|
+
if kind == "var":
|
|
500
|
+
self.take()
|
|
501
|
+
return AceVar(value)
|
|
502
|
+
if kind == "int":
|
|
503
|
+
self.take()
|
|
504
|
+
return int(value)
|
|
505
|
+
if kind == "real":
|
|
506
|
+
self.take()
|
|
507
|
+
return self.real(value)
|
|
508
|
+
if kind == "quoted":
|
|
509
|
+
self.take()
|
|
510
|
+
return _unquote(value)
|
|
511
|
+
if kind == "symbol":
|
|
512
|
+
# `-` directly before a number is a sign; any symbol is
|
|
513
|
+
# otherwise a bare symbolic atom (the operator argument of
|
|
514
|
+
# ``expr(+,...)`` / ``formula(...,=,...)``).
|
|
515
|
+
if value == "-" and self.peek(1)[0] in ("int", "real"):
|
|
516
|
+
self.take()
|
|
517
|
+
num_kind, num_value = self.peek()
|
|
518
|
+
self.take()
|
|
519
|
+
return (-int(num_value) if num_kind == "int"
|
|
520
|
+
else -self.real(num_value))
|
|
521
|
+
self.take()
|
|
522
|
+
return value
|
|
523
|
+
if kind == "atom":
|
|
524
|
+
self.take()
|
|
525
|
+
if self.peek() == ("punct", "("):
|
|
526
|
+
args = self.application_args()
|
|
527
|
+
return _wrap_term(value, args, self.text)
|
|
528
|
+
return value
|
|
529
|
+
raise AceDrsUnreadError(
|
|
530
|
+
f"DRS reader: a term cannot start with {value!r} "
|
|
531
|
+
f"(offset {self.tokens[self.i][2]})")
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def _unquote(quoted: str) -> str:
|
|
535
|
+
return quoted[1:-1].replace("''", "'")
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _real_value(number: Union[int, float]) -> Union[int, float]:
|
|
539
|
+
"""``number`` as the float that holds it exactly, else as the ``int`` itself (never a float that is another number)."""
|
|
540
|
+
if isinstance(number, float):
|
|
541
|
+
return number
|
|
542
|
+
try:
|
|
543
|
+
as_float = float(number)
|
|
544
|
+
except OverflowError:
|
|
545
|
+
return number
|
|
546
|
+
return as_float if as_float == number else number
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def _wrap_term(functor: str, args: List, text: str) -> AceTerm:
|
|
550
|
+
"""Specialize the wrapped-value terms; keep everything else verbatim."""
|
|
551
|
+
if functor == "named" and len(args) == 1 and isinstance(args[0], str):
|
|
552
|
+
return AceNamed(args[0])
|
|
553
|
+
if functor == "int" and len(args) == 1 and isinstance(args[0], int):
|
|
554
|
+
return AceInt(args[0])
|
|
555
|
+
if functor == "real" and len(args) == 1 and isinstance(args[0], (int, float)):
|
|
556
|
+
return AceReal(_real_value(args[0]))
|
|
557
|
+
if functor == "string" and len(args) == 1 and isinstance(args[0], str):
|
|
558
|
+
return AceString(args[0])
|
|
559
|
+
if functor == "expr" and len(args) == 3 and isinstance(args[0], str):
|
|
560
|
+
return AceExpr(args[0], args[1], args[2])
|
|
561
|
+
return AceTermApp(functor, tuple(args))
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def parse_ape_drs(text: str) -> AceDrs:
|
|
565
|
+
"""APE's printed DRS term → the 1:1 object model (see module docstring).
|
|
566
|
+
|
|
567
|
+
Raises :class:`AceDrsUnreadError` with offset and context on anything
|
|
568
|
+
that is not a well-formed term of the measured inventory.
|
|
569
|
+
"""
|
|
570
|
+
return _Parser(text.strip()).parse()
|