nativegate 0.1.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.
- nativegate/__init__.py +1 -0
- nativegate/__main__.py +4 -0
- nativegate/buildinfo.py +344 -0
- nativegate/cli.py +2007 -0
- nativegate/config.py +991 -0
- nativegate/declared_invariants.py +565 -0
- nativegate/discovery.py +167 -0
- nativegate/driverbuild.py +626 -0
- nativegate/drivers/__init__.py +5 -0
- nativegate/drivers/cpp.py +616 -0
- nativegate/drivers/fortran.py +507 -0
- nativegate/generators/__init__.py +0 -0
- nativegate/generators/cmake_gen.py +101 -0
- nativegate/generators/docker_gen.py +614 -0
- nativegate/generators/error_gen.py +104 -0
- nativegate/generators/f2py_gen.py +91 -0
- nativegate/generators/gateway_gen.py +110 -0
- nativegate/generators/golden_gen.py +50 -0
- nativegate/generators/k8s_gen.py +212 -0
- nativegate/generators/mcp_gen.py +281 -0
- nativegate/generators/middleware_gen.py +717 -0
- nativegate/generators/pybind_gen.py +406 -0
- nativegate/generators/pyproject_gen.py +61 -0
- nativegate/generators/python_pkg_gen.py +1164 -0
- nativegate/generators/test_gen.py +160 -0
- nativegate/golden.py +747 -0
- nativegate/invariants.py +532 -0
- nativegate/ir.py +789 -0
- nativegate/lattice.py +350 -0
- nativegate/locking.py +216 -0
- nativegate/oracle.py +904 -0
- nativegate/parsers/__init__.py +0 -0
- nativegate/parsers/cpp.py +105 -0
- nativegate/parsers/cpp_ast.py +1652 -0
- nativegate/parsers/cpp_regex.py +812 -0
- nativegate/parsers/fixed_form.py +868 -0
- nativegate/parsers/fortran.py +157 -0
- nativegate/parsers/fortran_fparser.py +1116 -0
- nativegate/parsers/fortran_regex.py +686 -0
- nativegate/preprocess.py +335 -0
- nativegate/structural_invariants.py +762 -0
- nativegate/suggest.py +208 -0
- nativegate/templates/__init__.py +20 -0
- nativegate/templates/golden_test_template.py +248 -0
- nativegate/wire.py +438 -0
- nativegate-0.1.0.dist-info/METADATA +547 -0
- nativegate-0.1.0.dist-info/RECORD +50 -0
- nativegate-0.1.0.dist-info/WHEEL +5 -0
- nativegate-0.1.0.dist-info/entry_points.txt +3 -0
- nativegate-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Fortran parser front door — picks a backend and hides the difference.
|
|
2
|
+
|
|
3
|
+
Two backends produce the identical `ir.ModuleIR`:
|
|
4
|
+
|
|
5
|
+
* `fortran_fparser` — a real fparser2 parse tree. The whole file is parsed
|
|
6
|
+
once by a standard-conforming Fortran parser, so routine boundaries,
|
|
7
|
+
declaration attributes, `result(...)` clauses, module nesting and
|
|
8
|
+
continuation lines are read off the tree instead of being reconstructed by
|
|
9
|
+
pattern. This is the parser design.md section 8 ultimately specifies.
|
|
10
|
+
* `fortran_regex` — the original line/regex reader, with `fixed_form.py` as
|
|
11
|
+
its fixed-form half. No grammar: each exposed name drives one targeted
|
|
12
|
+
search. Kept as the fallback for machines without fparser, and selectable
|
|
13
|
+
explicitly for reproducing its behaviour.
|
|
14
|
+
|
|
15
|
+
Selection order, first match wins:
|
|
16
|
+
|
|
17
|
+
1. the `backend=` argument,
|
|
18
|
+
2. `$NATIVEGATE_FORTRAN_PARSER` (`auto` | `fparser2` | `regex`),
|
|
19
|
+
3. `parser:` in nativegate.yaml, passed through by the CLI,
|
|
20
|
+
4. `auto`.
|
|
21
|
+
|
|
22
|
+
`auto` resolves to `fparser2` when fparser is importable, and to `regex`
|
|
23
|
+
otherwise — the same shape as the C++ side, where Clang is preferred when
|
|
24
|
+
available.
|
|
25
|
+
|
|
26
|
+
WHY THE DEFAULT WAS FLIPPED, AND THE ONE ARGUMENT THAT DECIDED IT
|
|
27
|
+
|
|
28
|
+
The worry about preferring a real grammar was never that it parses badly. It
|
|
29
|
+
was that fparser2 *correctly* refuses invalid Fortran the regex reader
|
|
30
|
+
silently accepted, so an existing service built on technically-invalid source
|
|
31
|
+
could start failing on upgrade. The parity harness could not answer that: it
|
|
32
|
+
only compares files both backends accept.
|
|
33
|
+
|
|
34
|
+
What answers it is that **nativegate must compile the source with gfortran
|
|
35
|
+
anyway** — `f2py -c` shells out to it. So any file gfortran rejects can never
|
|
36
|
+
produce a working service, whichever parser read it first. Refusing it at
|
|
37
|
+
parse time is then strictly better than mis-binding it and failing later, or
|
|
38
|
+
worse, succeeding with wrong intents.
|
|
39
|
+
|
|
40
|
+
That reduces the question to: does fparser2 refuse anything **gfortran
|
|
41
|
+
accepts**? Measured over 180 files — the nine real 1988–1997 fixed-form decks
|
|
42
|
+
in `libraries/petro`, plus numpy's own `f2py/tests/src` corpus, which exists
|
|
43
|
+
precisely to cover the edge cases f2py must survive — each parsed with
|
|
44
|
+
fparser2 and compiled with `gfortran -fsyntax-only`:
|
|
45
|
+
|
|
46
|
+
files where fparser2 is STRICTER than gfortran: 0
|
|
47
|
+
|
|
48
|
+
Every one of the 180 was accepted by both. The three cases that did have to
|
|
49
|
+
change during the parity work were *test fixtures*, not library code, and
|
|
50
|
+
gfortran rejects all three too (`data declaration statement cannot appear
|
|
51
|
+
after executable statements`, `Unclassifiable statement`) — the tests had been
|
|
52
|
+
encoding the regex reader's lack of a grammar as a requirement.
|
|
53
|
+
|
|
54
|
+
The escape hatch stays: `parser: regex` in nativegate.yaml, or
|
|
55
|
+
`NATIVEGATE_FORTRAN_PARSER=regex`, pins the old reader. A machine without
|
|
56
|
+
fparser installed keeps the regex reader and is unaffected.
|
|
57
|
+
|
|
58
|
+
Asking for `fparser2` explicitly and not having it is an error, not a silent
|
|
59
|
+
downgrade — the same rule the C++ side enforces. A build that quietly loses
|
|
60
|
+
symbols because a wheel was missing is exactly the failure this module exists
|
|
61
|
+
to prevent.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
from __future__ import annotations
|
|
65
|
+
|
|
66
|
+
import os
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
|
|
69
|
+
from ..config import ExposeConfig
|
|
70
|
+
from ..ir import ModuleIR
|
|
71
|
+
from . import fortran_fparser, fortran_regex
|
|
72
|
+
|
|
73
|
+
# Re-exported so callers that reach for the free-form continuation joiner (the
|
|
74
|
+
# CLI's source-rewriting paths, and a good number of tests) keep working after
|
|
75
|
+
# the implementation moved into fortran_regex.
|
|
76
|
+
from .fortran_regex import normalize_free_form # noqa: F401
|
|
77
|
+
|
|
78
|
+
BACKENDS = ("auto", "fparser2", "regex")
|
|
79
|
+
_ENV_VAR = "NATIVEGATE_FORTRAN_PARSER"
|
|
80
|
+
|
|
81
|
+
# `auto` -> this, when the backend is actually importable. See the module
|
|
82
|
+
# docstring for the evidence behind the flip.
|
|
83
|
+
_AUTO_BACKEND = "fparser2"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ParserUnavailable(RuntimeError):
|
|
87
|
+
"""Raised when the requested Fortran backend cannot run on this machine."""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def resolve_backend(backend: str | None = None) -> str:
|
|
91
|
+
"""Return "fparser2" or "regex" for a possibly-"auto" request."""
|
|
92
|
+
choice = (backend or os.environ.get(_ENV_VAR) or "auto").lower()
|
|
93
|
+
if choice not in BACKENDS:
|
|
94
|
+
raise ParserUnavailable(
|
|
95
|
+
f"Unknown Fortran parser backend '{choice}'; expected one of {', '.join(BACKENDS)}."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if choice == "regex":
|
|
99
|
+
return "regex"
|
|
100
|
+
|
|
101
|
+
if choice == "fparser2":
|
|
102
|
+
if not fortran_fparser.is_available():
|
|
103
|
+
raise ParserUnavailable(
|
|
104
|
+
"The fparser2 parser was requested but fparser is not usable: "
|
|
105
|
+
f"{fortran_fparser.unavailable_reason()}. Install it with "
|
|
106
|
+
'`pip install "nativegate[fparser]"`, or set parser: regex in '
|
|
107
|
+
"nativegate.yaml."
|
|
108
|
+
)
|
|
109
|
+
return "fparser2"
|
|
110
|
+
|
|
111
|
+
if _AUTO_BACKEND == "fparser2" and fortran_fparser.is_available():
|
|
112
|
+
return "fparser2"
|
|
113
|
+
return "regex"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def backend_description(backend: str | None = None) -> str:
|
|
117
|
+
"""One line naming the backend in use, for `ngate inspect`/`generate`."""
|
|
118
|
+
try:
|
|
119
|
+
resolved = resolve_backend(backend)
|
|
120
|
+
except ParserUnavailable as exc:
|
|
121
|
+
return str(exc)
|
|
122
|
+
requested = (backend or os.environ.get(_ENV_VAR) or "").lower()
|
|
123
|
+
if resolved == "fparser2":
|
|
124
|
+
version = fortran_fparser.fparser_version()
|
|
125
|
+
if requested == "fparser2":
|
|
126
|
+
return f"fparser2 parse tree ({version}, selected explicitly)"
|
|
127
|
+
return f"fparser2 parse tree ({version}, default)"
|
|
128
|
+
if requested == "regex":
|
|
129
|
+
return "regex reader (selected explicitly)"
|
|
130
|
+
# The only other way to land on regex is `auto` with no fparser installed.
|
|
131
|
+
# Say so plainly: this is a real capability difference, not a preference,
|
|
132
|
+
# and someone reading a build log should be able to tell which they got.
|
|
133
|
+
return (
|
|
134
|
+
"regex reader (no grammar; fparser unavailable: "
|
|
135
|
+
f"{fortran_fparser.unavailable_reason()}. Install with "
|
|
136
|
+
'`pip install "nativegate[fparser]"` for the parse-tree backend)'
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def parse_source(
|
|
141
|
+
path: Path,
|
|
142
|
+
expose: ExposeConfig,
|
|
143
|
+
include_paths: list[Path] | None = None,
|
|
144
|
+
*,
|
|
145
|
+
backend: str | None = None,
|
|
146
|
+
) -> ModuleIR:
|
|
147
|
+
"""Parse one Fortran source into a ModuleIR using the selected backend."""
|
|
148
|
+
if resolve_backend(backend) == "fparser2":
|
|
149
|
+
return fortran_fparser.parse_source(path, expose, include_paths)
|
|
150
|
+
return fortran_regex.parse_source(path, expose, include_paths)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def list_routine_names(path: Path, *, backend: str | None = None) -> list[str]:
|
|
154
|
+
"""Every function/subroutine name in the file, in source order."""
|
|
155
|
+
if resolve_backend(backend) == "fparser2":
|
|
156
|
+
return fortran_fparser.list_routine_names(path)
|
|
157
|
+
return fortran_regex.list_routine_names(path)
|