# Command Interpreter

`cmd.h`/`cmd.cpp` implement `CommandInterpreter`, the class that walks a parsed control block ([`PTControl`](ptcommand.md)) top to bottom and carries out what it says: elaborate the circuit, run analyses, and execute the small set of control-block commands (`var`, `options`, `save`, `alter`, `print`, `elaborate`, `clear`, `abort`, `postprocess`, `mc`/`endmc`). It is the layer directly above [analyses](index.md#8-analyses) and directly below [`main()`](main.md).

## Control block and entries

A control block is a `PTControl` - a vector of `ControlEntry` (`std::variant<PTAnalysis, PTCommand>`), in source order (see [`PTCommand`](ptcommand.md)). `CommandInterpreter::run()` iterates the entries starting at a given index (`at_`), dispatching each one:

- A [`PTAnalysis`](ptanalysis.md) entry triggers elaboration (if needed) and an analysis run.
- A `PTCommand` entry is looked up by name in the static `commandDescriptors` table and dispatched to its handler function.

`run()` returns an `InterpreterExitStatus`: `EndReached` (fell off the end normally), `Error`/`HardFault` (a command failed; see below), or `RequestMCExit` (an `endmc` command asked the caller to stop, used by `mc`'s nested loop).

## Command descriptors and dispatch

`CmdDesc` (nested in `CommandInterpreter`) describes one command's calling convention: minimum/maximum keyword count, minimum/maximum expression count, and an optional allow-list of keyword-argument names (`limitArgs`/`allowedArgs`). `commandDescriptors` is a static `unordered_map<Id, CmdDesc>` populated once with an entry per command name, each pointing at a free function of type `CommandFuncPtr`:

```
InterpreterExitStatus (*)(CommandInterpreter& interpreter, PTCommand& cmd, Status& s)
```

Before calling the handler, `run()` validates the command's shape against its descriptor (keyword/expression counts, allowed keyword-argument names) and returns `InterpreterExitStatus::HardFault` immediately on a mismatch, extending `Status` with the command's source [location](sourceloc.md). Handler implementations are the `cmd_*` free functions in `cmd.cpp` (`cmd_abort`, `cmd_clear`, `cmd_save`, `cmd_var`, `cmd_options`, `cmd_alter`, `cmd_elaborate`, `cmd_print`, `cmd_postprocess`, `cmd_mc`, `cmd_endmc`).

## Error handling: mask or abort

A failed handler (return value other than `OK`) does not necessarily stop the run: `mustAbort(cmd.name())` decides whether to escalate. Aborting is controlled by two pieces of state:

- `abortCommands` - a set of command names (`analysis` counts as a pseudo-command name for this purpose).
- `abortOnMatch` - whether membership in `abortCommands` means "abort" (`true`) or "don't abort" (`false`).

`addAbort()`/`clearAborts()`/`setAbortOnMatch()` are driven by the `abort` command (`cmd_abort`): `abort on cmd1 cmd2 ...` aborts only on the listed commands; `abort except cmd1 cmd2 ...` aborts on everything except the listed ones; `abort always`/`abort never` set the blanket cases without a list. When a masked failure occurs, the interpreter logs `Status`'s message via `Simulator::err()` and continues to the next entry instead of stopping. `HardFault` (a malformed command, or a masked `Error` escalated by `mustAbort()`) always stops `run()`.

## Elaboration helpers

`CommandInterpreter` never elaborates the circuit itself outside of these three helpers, called by both `run()` and several `cmd_*` handlers before they touch circuit state:

- **`defaultElaboration()`** - elaborates the default toplevel definition if the circuit is not elaborated yet ([`Circuit::Flags::Elaborated`](ptmodel.md) not set); otherwise a no-op.
- **`minimalElaboration()`** - elaborates the default toplevel if not elaborated yet, otherwise re-elaborates only what changed (`Circuit::elaborateChanges()`). This is what `run()` calls before every `PTAnalysis` entry, and what `print`/`elaborate changes` use.
- **`elaborate()`** - full elaboration from an explicit list of toplevel definition names, applying `userOptions_` as simulator options first. Backs `elaborate circuit(...)` and the two helpers above (with the built-in `__topdef__`/`__topinst__` names when no explicit list is given).

## Accumulated state

Between commands, `CommandInterpreter` accumulates state that later commands and analyses read:

- **`commonSaves_`** (`std::vector<PTSave>`) - save directives from `save` commands (`clearSaves()`/`addSaves()`), applied to every subsequent analysis via `Analysis::add()`.
- **`userOptions_`** (a [`PTParameterMap`](ptparameters.md)) - option values/expressions from `options` commands (`clearUserOptions()`/`addUserOption()`), applied at elaboration and to every subsequent analysis.
- **`analysisNamePrefix_`** - prepended to output file names of analyses that follow; set by `mc` for each sample (e.g. `mc1.3.`) and cleared afterward.
- **`printProgress_`/`runPostprocess_`** - toggles set by `main()` from command-line flags, read back by `postprocessingAllowed()` (gates `postprocess`) and by `run()` (gates progress-bar output around an analysis).

`clearVariables()` resets the circuit's variables and, if a Python executable is configured, sets the `PYTHON` circuit variable to `Platform::pythonExecutable()` (see [Platform Entry Support](platform.md)) - this is why a Verilog-A `postprocess` script's netlist-level `PYTHON` variable stays consistent with the interpreter's own subprocess launches.

## Running an analysis

For a `PTAnalysis` entry, `run()`:

1. Ensures the circuit is elaborated (`minimalElaboration()`).
2. Creates the analysis via `Analysis::create()` (see the [analyses](index.md#8-analyses) section); a creation failure is handled the same masked-or-abort way as a command failure, keyed on the pseudo-command name `analysis`.
3. Sets the analysis's output file name prefix, then feeds it `userOptions_` and `commonSaves_` via `Analysis::add()`.
4. Installs an [`AnalysisProgress`](progressbar.md) reporter (unless progress printing is off, or a Monte Carlo sample is in progress - nested progress bars would fight over the terminal).
5. Calls `Analysis::run()`, reports elapsed time or a final progress line, and deletes the analysis object.

## The `mc` command: a nested interpreter loop

`cmd_mc` implements Monte Carlo looping without a separate execution engine: it re-enters `CommandInterpreter::run()` from just after the `mc` statement, once per sample, and relies on a matching `endmc` command (`cmd_endmc`) to unwind that nested call via `InterpreterExitStatus::RequestMCExit`. Per sample, it:

1. Sets the `MCSAMPLE` circuit variable and the analysis file-name prefix (`<mcname>.<sample>.`).
2. Forces re-propagation of variables (`Circuit::Flags::VariablesChanged`) and re-elaborates changes, which is what actually draws a new Monte Carlo sample through the parameter evaluator's generators (see [Netlist Context of the Evaluator](rpnevalctx.md)'s `MCData`).
3. Runs the loop body by calling `run(loopStart, s)` recursively; a `HardFault` or falling off the end without `endmc` aborts the loop.

Nested `mc` loops are rejected up front (`circuit().paramEvaluator().mcData()!=nullptr`). Monte Carlo names must be unique across a run (`isUniqueMc()`, backed by the `mcNames` set); a name is claimed as soon as the `mc` command naming it is reached, whether or not the loop completes successfully. After the loop (success or abort), the parameter evaluator's `MCData` pointer is cleared and, on success, the circuit is re-elaborated once more with zero variation so later commands see the nominal (un-sampled) circuit state.

## Other commands

- **`var`** - evaluates all keyword-argument expressions first, then writes every value (plain and computed) as a circuit variable, so an expression on the right-hand side of one assignment cannot observe another assignment in the same `var` statement.
- **`options`** - stores keyword arguments (values and expressions alike) into `userOptions_`; they are not applied until the next elaboration.
- **`alter`** - sets instance or model parameters directly (`Circuit::setInstanceParameters()`/`setModelParameters()`) by name, after a default elaboration if none has happened yet.
- **`elaborate`** - `elaborate circuit(...)` (optionally with `topdef`/`topinst` keyword arguments) or `elaborate changes`.
- **`print`** - a grab-bag of diagnostic dumps selected by keyword (`devices`, `models`, `hierarchy`, `nodes`, `unknowns`, `tolerances`, `sparsity`, `stats`, `rpn`, an individual `instance`/`model`/`device` by name, ...), or, with no keyword, evaluates and prints its expression list (with configurable `eol`/`separator` keyword arguments).
- **`postprocess`** - runs an external program (first expression is the program name, the rest are arguments) via `runProcess()` (see [Process Utilities](processutils.md)), skipped entirely when `runPostprocess()` is false.
- **`clear`** - resets variables, saves, and/or options (all three if no keyword is given).

## Fitting into the run

`CommandInterpreter` is constructed once by [`main()`](main.md) around a `ParserTables&`, the top-level `PTControl&`, and a `Circuit&`, and its `run()` is called once from index `0`. All recursion (Monte Carlo loops) happens through the same `run()` entry point with a different starting index, keeping the whole control-block execution model - including nested loops - inside this one class.
