Skip to content

Commit 186f0af

Browse files
committed
codex: improve parser grammar slicing and scope docs
1 parent d43efca commit 186f0af

11 files changed

Lines changed: 2044 additions & 1132 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ recurses into direct children. Shared declaration helpers parse variables,
460460
procedure arguments/results, and type fields, then push them into the active
461461
scope. Procedure execution bodies and internal subprograms are ignored for
462462
wrapper metadata; procedure-local interfaces are retained for callback typing.
463+
Parameter variables keep both `value` (best resolved value) and runtime
464+
`symbolic_value` (the original expression) when the parser has that information.
463465

464466
The semantics layer consumes `FortranFile`/`FortranModule` objects and projects them into language-independent semantic IR (`SemanticModule`, `SemanticFunction`, `SemanticClass`, `SemanticType`). This keeps the semantic API model independent from parser internals, matching the project goal that parser output is a helper and the semantic interface/IR is the source of truth.
465467

fortran_parser.md

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ sections so maintainers can navigate the file by concern instead of by history:
8383

8484
- Regex/constants and parser-wide type aliases
8585
- Module-level helper blocks (source-form rules, preprocessor logic,
86-
diagnostics, shape evaluation, compile-time expression resolution,
87-
dependency ordering)
86+
diagnostics, compile-time expression resolution, dependency ordering)
8887
- `FortranParser` internals grouped by domain:
8988
- visitor-style API entrypoints (`visit_file`, `visit_project`,
9089
`visit_wrap_readiness`)
@@ -107,9 +106,71 @@ for wrapper metadata, and procedure-internal subprograms are not exported as
107106
file/module procedures. Procedure-local interface blocks are still visited
108107
enough to type callback dummy arguments and to preserve interface metadata.
109108

109+
### 2.1 Recursive parser sketch
110+
111+
Small input:
112+
113+
```fortran
114+
module m
115+
integer, parameter :: n = 4
116+
contains
117+
subroutine scale(x)
118+
real, intent(inout) :: x(n)
119+
end subroutine scale
120+
end module m
121+
```
122+
123+
The parser handles it in this order:
124+
125+
1. `visit_file` preprocesses the source and calls `_helper_slice_child_units`
126+
at file scope. The result is one `_SourceUnit`: `kind="module"`,
127+
`name="m"`, and `lines=[module m ... end module m]`.
128+
2. `visit_source_unit` dispatches that slice to `visit_module_unit`.
129+
3. `visit_module_unit` creates a module `_ParserScope`, calls
130+
`_helper_split_unit_parts`, and sends only the module specification lines to
131+
`_helper_visit_spec_part`.
132+
4. `_helper_visit_spec_part` uses the shared declaration backend:
133+
`_helper_parse_declaration_line` parses `integer, parameter :: n = 4`, then
134+
`_helper_push_declaration_to_scope` appends the resulting parameter variable
135+
to `FortranModule.variables`.
136+
5. The module visitor recursively slices direct children from its substring.
137+
It finds one procedure unit, `scale`, and dispatches it to
138+
`visit_procedure_unit`.
139+
6. `visit_procedure_unit` creates a procedure `_ParserScope`, splits the
140+
procedure into header/specification/execution/contains, and visits only the
141+
specification part. The same declaration backend parses
142+
`real, intent(inout) :: x(n)` and pushes the metadata into the procedure
143+
argument symbol table.
144+
145+
Scope is always an explicit argument to the shared helpers. That is the reason
146+
two modules can each define `type :: state` without conflict, while two
147+
same-level `module m` declarations or two same-level contained procedures with
148+
the same name are rejected by `_helper_validate_sibling_units`.
149+
150+
End-name validation is strict for structural units whose names define exported
151+
scope boundaries, such as modules, submodules, programs, interfaces, and
152+
derived types. Procedure end-name mismatches are still tolerated while slicing
153+
third-party sources because some accepted fixture code contains copy/paste
154+
procedure end labels; the procedure is closed by unit kind so parsing can
155+
continue, and duplicate procedure names are validated at the sibling scope.
156+
157+
The only separate specification-line visitors are grammar-specific:
158+
module-like units share `_helper_visit_module_like_spec_line`, procedures use
159+
`_helper_visit_procedure_spec_line` for `implicit`, `external`, `import`, and
160+
local `parameter` handling, and derived types use
161+
`_helper_visit_type_spec_line` for `sequence`, `private`, and type-bound
162+
declaration rules. All three still call the same declaration parser/pusher for
163+
actual declarations.
164+
110165
Most parser organization changes are structural, but behavior, model-schema,
111166
coverage, or fixture changes should be reflected in this reference.
112167

168+
Parameter constants expose both `value` and runtime `symbolic_value` when
169+
available. `value` is the parser's best resolved expression after compile-time
170+
folding; `symbolic_value` preserves the original parameter initializer for
171+
validation, debugging, and downstream diagnostics without changing the legacy
172+
JSON fixture shape.
173+
113174
## 3) Terminal usage and expected outputs
114175

115176
### 3.1 Basic CLI invocation

fortran_parser/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@ def __post_init__(self) -> None:
217217
if self.kind is None:
218218
self.kind = ""
219219

220+
@property
221+
def symbolic_value(self) -> str | None:
222+
return getattr(self, "_symbolic_value", None)
223+
224+
@symbolic_value.setter
225+
def symbolic_value(self, value: str | None) -> None:
226+
self._symbolic_value = value
227+
220228
@property
221229
def shape_info(self) -> list[dict[str, str | None]]:
222230
"""Structured per-dimension shape metadata derived from `shape` tokens."""

0 commit comments

Comments
 (0)