Just-in-Time (JIT) Parsing#

By default, MontePy defers the full parse of each input until the data are actually needed. On first read only a lightweight “JIT” parse is performed: enough to categorize the input and extract its number (for Numbered_MCNP_Object subclasses). The raw Input object is stored and the full parse is triggered on-demand.

How it Works#

  1. MCNP_Object.__init__ is called with jit_parse=True (the default).

  2. _jit_light_init instantiates self._JitParser(), parses only the first few tokens, and stores the results as instance attributes. The attribute _not_parsed = True is set as a marker.

  3. If the JIT parse raises any exception (including AttributeError when _JitParser is not defined), the exception is caught and a full parse is performed automatically as a fallback. JIT parsing is explicitly “fast and dirty” — correctness is guaranteed by this fallback, not by the JIT parser itself.

  4. When the full data are required, full_parse() is called. This re-runs __init__ with jit_parse=False, triggering a full parse via _parse_tree. Any attributes listed in _KEYS_TO_PRESERVE are carried over from the JIT state.

Warning

JIT parsers must be context-free (no state, no look-back). Do not add context-sensitive logic to a _JitParser. Keep it minimal: parse only what is needed to categorize the input and read its number. Anything that could fail gracefully will be handled by the automatic fallback to full parsing.

needs_full_ast vs needs_full_cst#

Two decorators are provided to trigger a full parse on demand. They differ in what kind of parsed data they signal is needed:

needs_full_ast()

Use on getters (reading data). Signals that an Abstract Syntax Tree (AST) — the semantic values — is sufficient.

needs_full_cst()

Use on setters and deleters (modifying data). Signals that a Concrete Syntax Tree (CST) — which preserves whitespace and comment formatting — is required so that the modified input can be written back faithfully.

Both decorators currently call self.full_parse() and are therefore functionally identical. The distinction is preserved as infrastructure for a possible future where JIT parsing can provide a partial AST (enough for reads) without yet building the full CST (needed for writes). Using the correct decorator now ensures that future optimization can be applied without changing call sites.

from montepy.utilities import needs_full_ast, needs_full_cst

@property
@needs_full_ast          # reading: AST is sufficient
def density(self):
    return self._density_node.value

@density.setter
@needs_full_cst          # writing: CST required for faithful round-trip output
def density(self, value):
    self._density_node.value = value

Supporting JIT Parsing in a New Class#

To add JIT support to a new class:

  1. Set _JitParser to the lightweight, context-free parser class that parses only the minimum needed tokens (number, classifier, etc.):

    class MyInput(MCNP_Object):
    
        @staticmethod
        def _parser():
            return MyFullParser()
    
        _JitParser = MyJitParser
    
  2. If any data must survive the JIT → full-parse transition (e.g., problem links established between the two steps), add those attribute names to _KEYS_TO_PRESERVE:

    _KEYS_TO_PRESERVE = {"_some_attr"}
    
  3. Decorate getters with @needs_full_ast and setters/deleters with @needs_full_cst.