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,231 @@
|
|
|
1
|
+
"""A numeral is a constant: the one name a writer gives it, and the check that keeps it apart.
|
|
2
|
+
|
|
3
|
+
On every route that was not asked for arithmetic, a :class:`~unicode_logic_kit.fol.nodes.Number`
|
|
4
|
+
is a CONSTANT symbol identified by its VALUE: ``Number(1)``, ``Number(1.0)`` and the
|
|
5
|
+
numeral written ``01`` are one constant (one node: a ``Number`` stores a float with a
|
|
6
|
+
whole value as the integer it equals), and
|
|
7
|
+
nothing else is known about it, so two numerals of different value may denote the same
|
|
8
|
+
element, like any two constants. The operators ``+ - * /`` are uninterpreted function
|
|
9
|
+
symbols and ``< > ≤ ≥`` uninterpreted predicates. A problem writer for a language whose
|
|
10
|
+
own numbers are arithmetic (TPTP's ``1`` is an ``$int``, ``$sum`` is addition) must not
|
|
11
|
+
hand a numeral to the prover as a number: Vampire and E read the bare ``1`` as an
|
|
12
|
+
integer, and the text ``p(1)`` is then a type error for a predicate over individuals,
|
|
13
|
+
while ``1 != 2`` and ``$sum(1,1) = 2`` are theorems of the prover's arithmetic that the
|
|
14
|
+
kit's reading does not have.
|
|
15
|
+
|
|
16
|
+
This module is what such a writer shares:
|
|
17
|
+
|
|
18
|
+
* :func:`numeral_name` -- the name a numeral is written under, one per VALUE: the text of
|
|
19
|
+
the number, with an integral float spelled as the integer (``1.0`` is ``1``). The writer
|
|
20
|
+
then treats it as a constant of that name, so the writer's ordinary renaming (a name
|
|
21
|
+
that is no word in the target language is replaced by a legal, collision-free token,
|
|
22
|
+
and the replacement is recorded) gives it its word, and the writer's name map reads it
|
|
23
|
+
back. :func:`numeral_value` is the inverse.
|
|
24
|
+
* :func:`numerals_as_constants` -- rewrite every numeral in term position as that
|
|
25
|
+
constant, and refuse by name a constant, sorted constant or function that is spelled
|
|
26
|
+
like a numeral of the problem (``Number(1)`` and ``Constant('1')``): they would be one
|
|
27
|
+
symbol under one name, and the kit keeps a numeral and a constant apart.
|
|
28
|
+
|
|
29
|
+
A ``Number`` that is the bound of a counting quantifier (``Count.n``,
|
|
30
|
+
``SortedCount.n``) is not a term and is left as it is. A writer whose target states a
|
|
31
|
+
comparison with a cardinality arithmetically (``|{x : P(x)}| ≥ 2`` is HOL's ``card {x. P x}
|
|
32
|
+
≥ 2`` over the naturals) asks for ``counting_comparisons=True``: a ``Number`` operand of
|
|
33
|
+
such a comparison is then the number of the comparison and not a constant, and is left as
|
|
34
|
+
it is too.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from dataclasses import replace
|
|
38
|
+
from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Sequence, Tuple, Union
|
|
39
|
+
|
|
40
|
+
from ._fol_nodes import _number_text
|
|
41
|
+
from .nodes import (
|
|
42
|
+
Atom, Cardinality, Constant, Count, Function, Node, Number, SortedCardinality,
|
|
43
|
+
SortedConstant, SortedCount,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
__all__ = ["numeral_name", "prefixed_numeral_name", "numeral_value", "term_numerals",
|
|
47
|
+
"numerals_as_constants"]
|
|
48
|
+
|
|
49
|
+
_COUNT_NODES = (Count, SortedCount)
|
|
50
|
+
|
|
51
|
+
#: The comparisons whose operands are numbers when one of them is a cardinality.
|
|
52
|
+
_COMPARISONS = frozenset({"=", "≠", "<", ">", "≤", "≥"})
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _plain(value: Union[int, float]) -> Union[int, float]:
|
|
56
|
+
"""``value`` as the ``int`` or ``float`` it is; a ``bool`` is the integer it equals."""
|
|
57
|
+
if isinstance(value, bool):
|
|
58
|
+
return int(value)
|
|
59
|
+
if isinstance(value, (int, float)):
|
|
60
|
+
return value
|
|
61
|
+
raise NotImplementedError(
|
|
62
|
+
f"Number({value!r}) holds a {type(value).__name__}, and only an int or a float is a "
|
|
63
|
+
"numeral the kit's writers can name; convert the value first.")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def numeral_name(value: Union[int, float]) -> str:
|
|
67
|
+
"""The name a numeral of value ``value`` is written under: ONE name per value.
|
|
68
|
+
|
|
69
|
+
The text of the number as the kit prints it everywhere (digits, an optional fractional
|
|
70
|
+
part, a leading ``-``, no exponent), except that a float with an integral value is
|
|
71
|
+
spelled as that integer, so ``1`` and ``1.0`` are one name
|
|
72
|
+
(``numeral_name(1.0) == numeral_name(1) == '1'``) while ``2.5`` stays ``'2.5'``. A
|
|
73
|
+
:class:`~unicode_logic_kit.fol.nodes.Number` already stores such a float as the integer, so for
|
|
74
|
+
the value of a node this is simply its printed text; the function takes a raw value too.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: ``value`` is ``inf``, ``-inf`` or ``nan``: no syntax of the kit has a
|
|
78
|
+
literal for it.
|
|
79
|
+
NotImplementedError: ``value`` is neither an ``int`` nor a ``float``.
|
|
80
|
+
"""
|
|
81
|
+
value = _plain(value)
|
|
82
|
+
if isinstance(value, float) and value.is_integer():
|
|
83
|
+
value = int(value)
|
|
84
|
+
return _number_text(value)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def prefixed_numeral_name(value: Union[int, float]) -> str:
|
|
88
|
+
""":func:`numeral_name` with an ``n`` in front (``n1``, ``n2.5``, ``n-1``): the spelling
|
|
89
|
+
the higher-order writers (THF, Isabelle/HOL) give a numeral, so that the identifier
|
|
90
|
+
their own sanitiser makes of it starts with a lower-case letter (``n1``, ``n2_5``,
|
|
91
|
+
``n_1``). One name per value, like :func:`numeral_name`."""
|
|
92
|
+
return "n" + numeral_name(value)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def numeral_value(name: str) -> Union[int, float]:
|
|
96
|
+
"""The value of the numeral that :func:`numeral_name` named ``name``: an ``int`` when the
|
|
97
|
+
name has no ``.``, a ``float`` otherwise.
|
|
98
|
+
|
|
99
|
+
The exact inverse of :func:`numeral_name` and of nothing else: the name of a float is the
|
|
100
|
+
shortest text that reads back as that float, so reading it back gives the float that was
|
|
101
|
+
named. A text a person typed is read by ``_numeral_from_text`` (at most 15 significant digits),
|
|
102
|
+
never by this function, and a name that :func:`numeral_name` does not produce is refused, so
|
|
103
|
+
two different names are never read as one value.
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
ValueError: ``name`` is not a name :func:`numeral_name` produces (``1.0``, ``007``, ``1e3``
|
|
107
|
+
and ``0.30000000000000005``, whose nearest float is named ``0.30000000000000004``,
|
|
108
|
+
are not).
|
|
109
|
+
"""
|
|
110
|
+
value = float(name) if "." in name else int(name)
|
|
111
|
+
if numeral_name(value) != name:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"{name!r} is not a name numeral_name produces (the numeral of that value is named "
|
|
114
|
+
f"{numeral_name(value)!r}), so it is not read as that value")
|
|
115
|
+
return value
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _is_counting_comparison(atom: Atom) -> bool:
|
|
119
|
+
"""Whether ``atom`` compares a cardinality (``|{x : φ}|``, sorted or not) with
|
|
120
|
+
something, by ``= ≠ < > ≤ ≥``: its ``Number`` operands are numbers, not individuals."""
|
|
121
|
+
return (atom.predicate in _COMPARISONS and len(atom.args) == 2
|
|
122
|
+
and any(isinstance(a, (Cardinality, SortedCardinality)) for a in atom.args))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _term_children(node: Node, counting_comparisons: bool = False) -> List[Node]:
|
|
126
|
+
"""The children of ``node`` that can hold a term: the bound of a counting quantifier
|
|
127
|
+
is not a term, and (with ``counting_comparisons``) neither is a number compared with a
|
|
128
|
+
cardinality; everything else is read as it is."""
|
|
129
|
+
if isinstance(node, _COUNT_NODES):
|
|
130
|
+
return [node.variable, node.formula]
|
|
131
|
+
if counting_comparisons and isinstance(node, Atom) and _is_counting_comparison(node):
|
|
132
|
+
return [a for a in node.args if not isinstance(a, Number)]
|
|
133
|
+
return node._child_nodes()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def term_numerals(formula: Node, *, counting_comparisons: bool = False) -> Iterator[Number]:
|
|
137
|
+
"""Every :class:`~unicode_logic_kit.fol.nodes.Number` of ``formula`` that stands in term
|
|
138
|
+
position, in pre-order: not the bound ``n`` of a ``Count`` / ``SortedCount`` and, with
|
|
139
|
+
``counting_comparisons=True``, not a ``Number`` compared with a cardinality."""
|
|
140
|
+
stack: List[Node] = [formula]
|
|
141
|
+
while stack:
|
|
142
|
+
node = stack.pop()
|
|
143
|
+
if isinstance(node, Number):
|
|
144
|
+
yield node
|
|
145
|
+
continue
|
|
146
|
+
stack.extend(reversed(_term_children(node, counting_comparisons)))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _spellings(numeral: Number, spell: Callable[[Union[int, float]], str]) -> Tuple[str, ...]:
|
|
150
|
+
"""The texts a numeral is written as: the name ``spell`` gives it and its canonical name.
|
|
151
|
+
|
|
152
|
+
A value has one spelling (a :class:`~unicode_logic_kit.fol.nodes.Number` stores a float with a
|
|
153
|
+
whole value as the integer it equals), so the text the kit prints for the node is the
|
|
154
|
+
canonical name and there is no third text."""
|
|
155
|
+
value = _plain(numeral.value)
|
|
156
|
+
return tuple(dict.fromkeys((spell(value), numeral_name(value))))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _clash(where: str, numeral: Number, spelling: str, kind: str, name: str) -> NotImplementedError:
|
|
160
|
+
return NotImplementedError(
|
|
161
|
+
f"{where}: the number {_number_text(_plain(numeral.value))} and the {kind} {name!r} are spelled "
|
|
162
|
+
f"alike ({spelling!r}), so they would be written under one name and be ONE symbol. "
|
|
163
|
+
"A numeral is a constant of its own, identified by its value (1 and 1.0 are one "
|
|
164
|
+
"constant), and it is not the constant of the same spelling -- the kit refuses to "
|
|
165
|
+
f"merge the two. Rename the {kind}.")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _check_names(formulas: Sequence[Node], spelled: Dict[str, Number], where: str) -> None:
|
|
169
|
+
for formula in formulas:
|
|
170
|
+
for node in formula.walk():
|
|
171
|
+
if isinstance(node, (Constant, SortedConstant)):
|
|
172
|
+
kind = "constant"
|
|
173
|
+
elif isinstance(node, Function):
|
|
174
|
+
kind = "function"
|
|
175
|
+
else:
|
|
176
|
+
continue
|
|
177
|
+
numeral = spelled.get(node.name)
|
|
178
|
+
if numeral is not None:
|
|
179
|
+
raise _clash(where, numeral, node.name, kind, node.name)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _rewrite(node: Node, spell: Callable[[Union[int, float]], str],
|
|
183
|
+
counting_comparisons: bool = False) -> Node:
|
|
184
|
+
if isinstance(node, Number):
|
|
185
|
+
return Constant(spell(_plain(node.value)))
|
|
186
|
+
if isinstance(node, _COUNT_NODES):
|
|
187
|
+
return replace(node, formula=_rewrite(node.formula, spell, counting_comparisons)) # the bound variable holds no numeral
|
|
188
|
+
if counting_comparisons and isinstance(node, Atom) and _is_counting_comparison(node):
|
|
189
|
+
return Atom(node.predicate, tuple(a if isinstance(a, Number) else _rewrite(a, spell, True)
|
|
190
|
+
for a in node.args))
|
|
191
|
+
return node.map_children(lambda child: _rewrite(child, spell, counting_comparisons))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def numerals_as_constants(formulas: Iterable[Node], *, where: str,
|
|
195
|
+
spell: Callable[[Union[int, float]], str] = numeral_name,
|
|
196
|
+
counting_comparisons: bool = False
|
|
197
|
+
) -> Tuple[List[Node], FrozenSet[str]]:
|
|
198
|
+
"""``formulas`` with every numeral in term position written as a constant, and the set
|
|
199
|
+
of the names of those constants.
|
|
200
|
+
|
|
201
|
+
The constant of a numeral is named ``spell(value)``: :func:`numeral_name` unless a writer
|
|
202
|
+
has its own spelling (the HOL writers spell a numeral ``n1``, ``n2.5``), which is
|
|
203
|
+
one name per value either way. A formula without a numeral comes back as the very same
|
|
204
|
+
object. ``where`` is the name of the writer that is calling, which a refusal opens with.
|
|
205
|
+
With ``counting_comparisons=True`` a ``Number`` that is an operand of a comparison with a
|
|
206
|
+
cardinality (``|{x : P(x)}| ≥ 2``) is not in term position: it is the number the target
|
|
207
|
+
compares the count with, and stays a ``Number``.
|
|
208
|
+
|
|
209
|
+
Raises:
|
|
210
|
+
NotImplementedError: a ``Constant``, ``SortedConstant`` or ``Function`` of the
|
|
211
|
+
formulas is spelled like a numeral of the formulas (``Number(1)`` next to
|
|
212
|
+
``Constant('1')``, or ``Number(2.5)`` next to ``Constant('2.5')``; ``Number(1.0)``
|
|
213
|
+
is ``Number(1)``, so ``Constant('1.0')`` is spelled like no numeral), or a
|
|
214
|
+
``Number`` holds a value that is neither an ``int`` nor a ``float``.
|
|
215
|
+
ValueError: a numeral is ``inf``, ``-inf`` or ``nan``.
|
|
216
|
+
"""
|
|
217
|
+
formulas = list(formulas)
|
|
218
|
+
spelled: Dict[str, Number] = {}
|
|
219
|
+
numerals_of: List[List[Number]] = []
|
|
220
|
+
for formula in formulas:
|
|
221
|
+
found = list(term_numerals(formula, counting_comparisons=counting_comparisons))
|
|
222
|
+
numerals_of.append(found)
|
|
223
|
+
for numeral in found:
|
|
224
|
+
for spelling in _spellings(numeral, spell):
|
|
225
|
+
spelled.setdefault(spelling, numeral)
|
|
226
|
+
if not spelled:
|
|
227
|
+
return formulas, frozenset()
|
|
228
|
+
_check_names(formulas, spelled, where)
|
|
229
|
+
names = frozenset(spell(_plain(n.value)) for found in numerals_of for n in found)
|
|
230
|
+
return [_rewrite(f, spell, counting_comparisons) if found else f
|
|
231
|
+
for f, found in zip(formulas, numerals_of)], names
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""Second-order quantifier AST node: ∀P / ∃P binding a predicate variable.
|
|
2
|
+
|
|
3
|
+
This node extends the classical FOL AST with second-order quantification over a
|
|
4
|
+
predicate variable. It is purely syntactic in this phase: there is no semantics
|
|
5
|
+
yet, and it rejects the first-order export back-ends (to_z3/to_prover9/to_tptp) —
|
|
6
|
+
second-order quantification is not first-order and is not SMT-expressible, so a
|
|
7
|
+
formula must first be evaluated by the finite-model evaluator (a later phase).
|
|
8
|
+
|
|
9
|
+
SecondOrderQuantifier is a frozen dataclass (hashable) subclassing Node. The
|
|
10
|
+
bound symbol is a PREDICATE name (uppercase, e.g. ``P``), not an object variable,
|
|
11
|
+
so binding a predicate name leaves the object-level free variables untouched:
|
|
12
|
+
free_variables walks _child_nodes() (only the ``formula`` child) and the scalar
|
|
13
|
+
``predicate``/``arity`` fields are invisible to it. Structural methods
|
|
14
|
+
(to_msfol, _relativize, traversal) are inherited unchanged because
|
|
15
|
+
Node.map_children / Node._child_nodes treat ``formula`` as the single Node child
|
|
16
|
+
and copy the scalar ``type``/``predicate``/``arity`` fields verbatim. The Unicode
|
|
17
|
+
and LaTeX renderers live in _msfl_nodes.py and dispatch by class name;
|
|
18
|
+
serialisation, the tree label, and export-rejection live here.
|
|
19
|
+
|
|
20
|
+
The bound predicate's ``arity`` is INFERRED from its applications in the body at
|
|
21
|
+
parse time (see ``_second_order_quantifier_transform`` / ``_infer_so_arity`` below);
|
|
22
|
+
it is recorded on the node for the semantics phase but is NOT printed by the
|
|
23
|
+
renderers, because it is
|
|
24
|
+
re-inferred on re-parse — keeping the parse → to_unicode_str → parse round-trip
|
|
25
|
+
stable.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
|
|
30
|
+
from ._fol_nodes import Node, Z3Env, NODE_CLASSES, Atom, register_parser_op
|
|
31
|
+
from .naming import ParsingError
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Shared message for the export back-ends: second-order quantification has no
|
|
35
|
+
# first-order / SMT encoding.
|
|
36
|
+
_NO_EXPORT = (
|
|
37
|
+
"Second-order quantification is not first-order and is not SMT-expressible; "
|
|
38
|
+
"it has no direct to_z3/to_prover9/to_tptp export. Evaluate the formula with "
|
|
39
|
+
"the finite-model evaluator instead."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class SecondOrderQuantifier(Node):
|
|
45
|
+
"""A second-order quantifier ∀P / ∃P binding a predicate variable.
|
|
46
|
+
|
|
47
|
+
type is "∀" (universal) or "∃" (existential). predicate is the bound
|
|
48
|
+
predicate-variable NAME (uppercase, as it appears applied in the body).
|
|
49
|
+
arity is that predicate variable's arity, inferred from its applications in
|
|
50
|
+
the body (0 for a never-applied / propositional predicate variable).
|
|
51
|
+
formula is the body.
|
|
52
|
+
|
|
53
|
+
Semantics: ∀P φ asserts φ holds for every interpretation of the
|
|
54
|
+
arity-``arity`` predicate P over the domain; ∃P φ for some interpretation.
|
|
55
|
+
Evaluation is a later (finite-model) phase.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
type: str
|
|
59
|
+
predicate: str
|
|
60
|
+
arity: int
|
|
61
|
+
formula: Node
|
|
62
|
+
|
|
63
|
+
def _tree_parts(self):
|
|
64
|
+
"""Return the ``∀ P/2`` style label and the single subformula child."""
|
|
65
|
+
return f"{self.type} {self.predicate}/{self.arity}", [self.formula]
|
|
66
|
+
|
|
67
|
+
def to_dict(self):
|
|
68
|
+
"""Serialise to dict with type tag, quantifier type, bound predicate, arity, and body."""
|
|
69
|
+
return {
|
|
70
|
+
"_type": "SecondOrderQuantifier",
|
|
71
|
+
"type": self.type,
|
|
72
|
+
"predicate": self.predicate,
|
|
73
|
+
"arity": self.arity,
|
|
74
|
+
"formula": self.formula.to_dict(),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def from_dict(d):
|
|
79
|
+
"""Deserialise a SecondOrderQuantifier from a dict produced by to_dict."""
|
|
80
|
+
return SecondOrderQuantifier(
|
|
81
|
+
d["type"],
|
|
82
|
+
d["predicate"],
|
|
83
|
+
d["arity"],
|
|
84
|
+
Node.from_dict(d["formula"]),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def to_z3(self, env: Z3Env = None):
|
|
88
|
+
"""Reject Z3 export: second-order quantification is not SMT-expressible."""
|
|
89
|
+
raise NotImplementedError(_NO_EXPORT)
|
|
90
|
+
|
|
91
|
+
def to_prover9(self) -> str:
|
|
92
|
+
"""Reject Prover9 export: second-order quantification is not first-order."""
|
|
93
|
+
raise NotImplementedError(_NO_EXPORT)
|
|
94
|
+
|
|
95
|
+
def to_tptp(self) -> str:
|
|
96
|
+
"""Reject TPTP export: second-order quantification is not first-order."""
|
|
97
|
+
raise NotImplementedError(_NO_EXPORT)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# =========================
|
|
101
|
+
# Registry extension
|
|
102
|
+
# =========================
|
|
103
|
+
|
|
104
|
+
NODE_CLASSES.update({
|
|
105
|
+
"SecondOrderQuantifier": SecondOrderQuantifier,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# =========================
|
|
110
|
+
# Arity inference + parser registration (second-order mode)
|
|
111
|
+
# =========================
|
|
112
|
+
#
|
|
113
|
+
# The bound predicate variable's arity is not in the surface syntax; it is
|
|
114
|
+
# inferred from the predicate's applications in the body at parse time. These two
|
|
115
|
+
# names (the inference helper and its error) live here, next to the node, and are
|
|
116
|
+
# re-exported by msflparser for backward compatibility.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class ConflictingArityError(ParsingError):
|
|
120
|
+
"""Raised when a second-order-bound predicate is applied at conflicting arities.
|
|
121
|
+
|
|
122
|
+
Subclasses ParsingError so callers catching the parser's error type also
|
|
123
|
+
catch this inference-level failure. It is constructed directly (not from a
|
|
124
|
+
Lark exception), so it bypasses ParsingError.__init__ and sets its own
|
|
125
|
+
message.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, predname: str, arities):
|
|
129
|
+
arities_str = ", ".join(str(a) for a in arities)
|
|
130
|
+
message = (
|
|
131
|
+
f"SYNTAX_ERROR: Second-order predicate variable '{predname}' is "
|
|
132
|
+
f"applied at conflicting arities ({arities_str}); a bound predicate "
|
|
133
|
+
f"variable must be used at a single, consistent arity."
|
|
134
|
+
)
|
|
135
|
+
self.args = (message,)
|
|
136
|
+
|
|
137
|
+
def __str__(self):
|
|
138
|
+
return self.args[0]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _infer_so_arity(body: Node, predname: str) -> int:
|
|
142
|
+
"""Infer the arity of a second-order-bound predicate from its uses in body.
|
|
143
|
+
|
|
144
|
+
Scans ``body`` for ``Atom(predname, args)`` occurrences and returns the
|
|
145
|
+
common application arity (``len(args)``). All applications of ``predname``
|
|
146
|
+
must share the same arity, else a ConflictingArityError is raised. A predicate
|
|
147
|
+
that is never applied has arity 0 (a propositional / Boolean predicate
|
|
148
|
+
variable).
|
|
149
|
+
|
|
150
|
+
Shadowing: an inner ``SecondOrderQuantifier`` that rebinds the SAME predicate
|
|
151
|
+
name introduces a fresh binding, so its body is NOT scanned for the outer
|
|
152
|
+
binder's arity — the descent stops at such a rebinder. (An inner binder over a
|
|
153
|
+
DIFFERENT name is descended into normally.)
|
|
154
|
+
"""
|
|
155
|
+
arities: set = set()
|
|
156
|
+
|
|
157
|
+
def visit(node: Node) -> None:
|
|
158
|
+
if isinstance(node, SecondOrderQuantifier) and node.predicate == predname:
|
|
159
|
+
return # inner binder shadows predname; do not descend into its scope
|
|
160
|
+
if isinstance(node, Atom) and node.predicate == predname:
|
|
161
|
+
arities.add(len(node.args))
|
|
162
|
+
for child in node._child_nodes():
|
|
163
|
+
visit(child)
|
|
164
|
+
|
|
165
|
+
visit(body)
|
|
166
|
+
|
|
167
|
+
if len(arities) > 1:
|
|
168
|
+
raise ConflictingArityError(predname, sorted(arities))
|
|
169
|
+
return arities.pop() if arities else 0
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _second_order_quantifier_transform(items):
|
|
173
|
+
"""Build a SecondOrderQuantifier from [FORALL/EXISTS, PREDICATE, body].
|
|
174
|
+
|
|
175
|
+
The PREDICATE token has no terminal handler (raw Token), the body is already a
|
|
176
|
+
built AST node (Lark transforms bottom-up), and the bound predicate's arity is
|
|
177
|
+
inferred from its applications in the body.
|
|
178
|
+
"""
|
|
179
|
+
quant = str(items[0])
|
|
180
|
+
predname = str(items[1])
|
|
181
|
+
body = items[2]
|
|
182
|
+
# Imported at call time, not at module scope: _ho_nodes is defined ON TOP of
|
|
183
|
+
# this module (it imports SecondOrderQuantifier and _infer_so_arity), so the
|
|
184
|
+
# dependency only closes once both are loaded — which, by the time any
|
|
185
|
+
# formula is parsed, they are. In a second-order body infer_bound_arity IS
|
|
186
|
+
# _infer_so_arity; it differs only where a property actually stands in an
|
|
187
|
+
# argument slot, which the second-order grammar cannot even express.
|
|
188
|
+
from ._ho_nodes import infer_bound_arity
|
|
189
|
+
arity = infer_bound_arity(body, predname)
|
|
190
|
+
return SecondOrderQuantifier(quant, predname, arity, body)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ∀P / ∃P over a PREDICATE variable, at the quantifier level. The classical
|
|
194
|
+
# first-order quantifier (∀x / ∃x -> quantifier_) is registered for the
|
|
195
|
+
# "second_order" mode in _fol_nodes.py; this adds the second alternative (the
|
|
196
|
+
# grammar's two-alternative quantifier rule).
|
|
197
|
+
register_parser_op(SecondOrderQuantifier, "second_order", "quantifier",
|
|
198
|
+
"second_order_quantifier_",
|
|
199
|
+
"(FORALL | EXISTS) PREDICATE prefix",
|
|
200
|
+
_second_order_quantifier_transform)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Shared symbol-name de-collision for the HOL / THF / Isabelle exporters.
|
|
2
|
+
|
|
3
|
+
Every exporter that sanitises predicate / constant / function names into a target
|
|
4
|
+
syntax must guarantee that **distinct source symbols never collapse to one emitted
|
|
5
|
+
name** — otherwise a non-valid formula could be emitted as a tautology (e.g. ``□Ab →
|
|
6
|
+
□ab`` collapsing to ``□ab → □ab``) or produce duplicate / ill-typed declarations. This
|
|
7
|
+
module factors out the two pieces that were duplicated across those exporters:
|
|
8
|
+
|
|
9
|
+
- :func:`dedupe` — assign a unique identifier by suffixing ``_2``, ``_3``, …;
|
|
10
|
+
- :class:`SymbolNames` — collect every ``(kind, name, arity)`` symbol of a formula and
|
|
11
|
+
map each to a unique functor, keyed so a predicate used at two arities is two symbols.
|
|
12
|
+
Parameterised by the target's base sanitiser and its alias map, so the THF and
|
|
13
|
+
Isabelle resolvers are one shared implementation.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import Callable
|
|
17
|
+
|
|
18
|
+
from .nodes import Atom, Constant, Number, Function, Node
|
|
19
|
+
from ._numeral_symbols import prefixed_numeral_name
|
|
20
|
+
from ._truth_constants import truth_value
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def dedupe(base: str, used: set) -> str:
|
|
24
|
+
"""Return ``base`` (or ``base_2``, ``base_3``, …) not already in ``used``; mark it used."""
|
|
25
|
+
name, i = base, 2
|
|
26
|
+
while name in used:
|
|
27
|
+
name, i = f"{base}_{i}", i + 1
|
|
28
|
+
used.add(name)
|
|
29
|
+
return name
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SymbolNames:
|
|
33
|
+
"""Map each distinct ``(kind, name, arity)`` symbol of ``formula`` to a unique name.
|
|
34
|
+
|
|
35
|
+
``sanitize`` turns a raw name into a (possibly non-injective) target identifier;
|
|
36
|
+
``alias`` is the set/mapping of names that should claim their natural sanitised form
|
|
37
|
+
first (e.g. ``=`` → ``feq``), so a user symbol that sanitises to the same stem is
|
|
38
|
+
pushed off it rather than the other way round. Predicates are keyed by
|
|
39
|
+
``(name, arity)`` so a predicate used at two arities yields two distinct symbols.
|
|
40
|
+
|
|
41
|
+
``reserved`` pre-claims the target's OWN fixed identifiers (built-in functors,
|
|
42
|
+
relation names, defined operators), so a user symbol that sanitises onto one of
|
|
43
|
+
them is pushed to a suffixed variant instead of silently colliding with — and
|
|
44
|
+
re-typing — a built-in (e.g. a user predicate named ``r`` colliding with the THF
|
|
45
|
+
accessibility relation ``r``, which would emit two conflicting declarations).
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, formula: Node, sanitize: Callable[[str], str], alias=frozenset(),
|
|
49
|
+
reserved=frozenset()):
|
|
50
|
+
preds, consts, funcs = set(), set(), set()
|
|
51
|
+
for n in formula.walk():
|
|
52
|
+
if isinstance(n, Atom):
|
|
53
|
+
if truth_value(n) is not None:
|
|
54
|
+
continue # `$true` / `$false` are written as the target's own constants
|
|
55
|
+
preds.add((n.predicate, len(n.args)))
|
|
56
|
+
elif isinstance(n, Constant):
|
|
57
|
+
consts.add(n.name)
|
|
58
|
+
elif isinstance(n, Number):
|
|
59
|
+
# one constant per VALUE (``1`` and ``1.0`` are ``n1``); a writer that must also
|
|
60
|
+
# refuse a constant spelled ``n1`` rewrites its numerals first
|
|
61
|
+
# (:func:`~unicode_logic_kit.fol._numeral_symbols.numerals_as_constants`)
|
|
62
|
+
consts.add(prefixed_numeral_name(n.value))
|
|
63
|
+
elif isinstance(n, Function):
|
|
64
|
+
funcs.add((n.name, len(n.args)))
|
|
65
|
+
used: set = set(reserved)
|
|
66
|
+
self.pred, self.const, self.func = {}, {}, {}
|
|
67
|
+
for key in sorted(preds, key=lambda k: (k[0] not in alias, k)):
|
|
68
|
+
self.pred[key] = dedupe(sanitize(key[0]), used)
|
|
69
|
+
for name in sorted(consts):
|
|
70
|
+
self.const[name] = dedupe(sanitize(name), used)
|
|
71
|
+
for key in sorted(funcs):
|
|
72
|
+
self.func[key] = dedupe(sanitize(key[0]), used)
|
|
73
|
+
|
|
74
|
+
def atom(self, node: Atom) -> str:
|
|
75
|
+
return self.pred[(node.predicate, len(node.args))]
|
|
76
|
+
|
|
77
|
+
def constant(self, name: str) -> str:
|
|
78
|
+
return self.const[name]
|
|
79
|
+
|
|
80
|
+
def function(self, node) -> str:
|
|
81
|
+
return self.func[(node.name, len(node.args))]
|