Add pyngspice Python bindings with targeted capacitor probing
Initial commit of the pyngspice layer on top of ngspice: - pybind11 C++ bindings (src/cpp/, src/bindings/) - Python package with SpiceRunner interface, netlist preprocessor - Targeted capacitor probing via .save i(C_name) directives for fast simulation when only specific cap currents are needed - Build system (CMakeLists.txt, pyproject.toml, build_mingw.py) - Test suite with 70 tests covering all preprocessor features - Examples, winflexbison tooling, CLAUDE.md project docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>pre-master-46
-
18.gitignore
-
241CLAUDE.md
-
436CMakeLists.txt
-
210build_mingw.py
-
93examples/python/ac_analysis.py
-
94examples/python/simple_rc.py
-
85pyngspice/__init__.py
-
518pyngspice/netlist.py
-
444pyngspice/runner.py
-
79pyproject.toml
-
340src/bindings/module.cpp
-
101src/cpp/callbacks.cpp
-
100src/cpp/callbacks.h
-
341src/cpp/raw_read.cpp
-
183src/cpp/raw_read.h
-
286src/cpp/sim_runner.cpp
-
219src/cpp/sim_runner.h
-
433src/cpp/simulator.cpp
-
298src/cpp/simulator.h
-
130src/cpp/trace.cpp
-
129src/cpp/trace.h
-
15src/maths/cmaths/cmath1.c
-
0tests/__init__.py
-
69tests/conftest.py
-
817tests/test_netlist.py
-
127tests/test_runner.py
-
69tests/test_sim.py
-
220tools/winflexbison/FlexLexer.h
-
34tools/winflexbison/README.md
-
166tools/winflexbison/changelog.md
-
173tools/winflexbison/custom_build_rules/README.md
-
BINtools/winflexbison/custom_build_rules/docs/1.png
-
BINtools/winflexbison/custom_build_rules/docs/2.png
-
BINtools/winflexbison/custom_build_rules/docs/3.png
-
BINtools/winflexbison/custom_build_rules/docs/4.png
-
BINtools/winflexbison/custom_build_rules/docs/5.png
-
BINtools/winflexbison/custom_build_rules/docs/6.png
-
BINtools/winflexbison/custom_build_rules/docs/BisonProperties.png
-
BINtools/winflexbison/custom_build_rules/docs/FlexProperties.png
-
BINtools/winflexbison/custom_build_rules/docs/Flex_debuging.png
-
BINtools/winflexbison/custom_build_rules/docs/Properties.png
-
BINtools/winflexbison/custom_build_rules/docs/Verbosity.png
-
23tools/winflexbison/custom_build_rules/win_bison_only/win_bison_custom_build.props
-
91tools/winflexbison/custom_build_rules/win_bison_only/win_bison_custom_build.targets
-
281tools/winflexbison/custom_build_rules/win_bison_only/win_bison_custom_build.xml
-
43tools/winflexbison/custom_build_rules/win_flex_bison/win_flex_bison_custom_build.props
-
178tools/winflexbison/custom_build_rules/win_flex_bison/win_flex_bison_custom_build.targets
-
521tools/winflexbison/custom_build_rules/win_flex_bison/win_flex_bison_custom_build.xml
-
23tools/winflexbison/custom_build_rules/win_flex_only/win_flex_custom_build.props
-
94tools/winflexbison/custom_build_rules/win_flex_only/win_flex_custom_build.targets
-
243tools/winflexbison/custom_build_rules/win_flex_only/win_flex_custom_build.xml
-
227tools/winflexbison/data/README.md
-
61tools/winflexbison/data/bison-default.css
-
58tools/winflexbison/data/local.mk
-
362tools/winflexbison/data/m4sugar/foreach.m4
-
3329tools/winflexbison/data/m4sugar/m4sugar.m4
-
1241tools/winflexbison/data/skeletons/bison.m4
-
27tools/winflexbison/data/skeletons/c++-skel.m4
-
778tools/winflexbison/data/skeletons/c++.m4
-
72tools/winflexbison/data/skeletons/c-like.m4
-
27tools/winflexbison/data/skeletons/c-skel.m4
-
1125tools/winflexbison/data/skeletons/c.m4
-
26tools/winflexbison/data/skeletons/d-skel.m4
-
628tools/winflexbison/data/skeletons/d.m4
-
2763tools/winflexbison/data/skeletons/glr.c
-
397tools/winflexbison/data/skeletons/glr.cc
-
3533tools/winflexbison/data/skeletons/glr2.cc
-
27tools/winflexbison/data/skeletons/java-skel.m4
-
502tools/winflexbison/data/skeletons/java.m4
-
1633tools/winflexbison/data/skeletons/lalr1.cc
-
1326tools/winflexbison/data/skeletons/lalr1.d
-
1303tools/winflexbison/data/skeletons/lalr1.java
-
380tools/winflexbison/data/skeletons/location.cc
-
157tools/winflexbison/data/skeletons/stack.hh
-
2tools/winflexbison/data/skeletons/traceon.m4
-
525tools/winflexbison/data/skeletons/variant.hh
-
2209tools/winflexbison/data/skeletons/yacc.c
-
105tools/winflexbison/data/xslt/bison.xsl
-
401tools/winflexbison/data/xslt/xml2dot.xsl
-
572tools/winflexbison/data/xslt/xml2text.xsl
-
765tools/winflexbison/data/xslt/xml2xhtml.xsl
-
4visualc/src/include/ngspice/config.h
@ -0,0 +1,241 @@ |
|||||
|
# CLAUDE.md - pyngspice Build Guide |
||||
|
|
||||
|
pyngspice provides native Python bindings for the ngspice circuit simulator using pybind11. |
||||
|
It serves as the ngspice backend for pyTesla (Tesla coil simulator), providing a SpiceRunner |
||||
|
interface that's a drop-in alternative to LTspice. |
||||
|
|
||||
|
## Building and Installation |
||||
|
|
||||
|
### Quick Start |
||||
|
```bash |
||||
|
python build_mingw.py |
||||
|
``` |
||||
|
|
||||
|
### What this does |
||||
|
1. Sets MinGW environment (PATH, CC, CXX) to avoid Git MSYS conflicts |
||||
|
2. Builds C++ extension via scikit-build-core + CMake + pybind11 |
||||
|
3. Installs in editable mode (Python changes = instant, C++ changes = rebuild) |
||||
|
4. Syncs to configured venvs (if SYNC_VENVS is set in build_mingw.py) |
||||
|
5. Verifies the build by importing pyngspice |
||||
|
|
||||
|
### Requirements |
||||
|
- MinGW-w64 GCC 15.x at `C:\mingw64` |
||||
|
- CMake 3.18+ (at `C:\Program Files\CMake\bin`) |
||||
|
- Python 3.9+ with development headers |
||||
|
- NumPy 1.20+ |
||||
|
|
||||
|
### After C++ changes: `python build_mingw.py` |
||||
|
### After Python changes: nothing (editable install) |
||||
|
|
||||
|
### Build Variants |
||||
|
```bash |
||||
|
python build_mingw.py # Standard build |
||||
|
python build_mingw.py --clean # Clean + rebuild from scratch |
||||
|
python build_mingw.py --debug # Debug configuration |
||||
|
python build_mingw.py --sync # Sync .pth files to other venvs |
||||
|
python build_mingw.py --verify # Just check if install works |
||||
|
``` |
||||
|
|
||||
|
## How It Compiles |
||||
|
|
||||
|
Three layers turn C++ into Python: |
||||
|
|
||||
|
``` |
||||
|
pyproject.toml scikit-build-core (PEP 517 build backend) |
||||
|
↓ |
||||
|
CMakeLists.txt CMake (finds Python, pybind11, compiles ~2000 ngspice C sources + wrapper C++) |
||||
|
↓ |
||||
|
src/bindings/module.cpp pybind11 (generates _pyngspice.pyd that Python can import) |
||||
|
``` |
||||
|
|
||||
|
`pip install -e .` triggers the whole chain. The `editable.mode = "inplace"` setting |
||||
|
in pyproject.toml is critical — it makes CMake build the .pyd directly into `pyngspice/`, |
||||
|
next to `__init__.py`. Without this, scikit-build-core uses fragile import hooks. |
||||
|
|
||||
|
## Architecture |
||||
|
|
||||
|
### Directory Structure |
||||
|
``` |
||||
|
pyngspice/ # Python package (importable, no rebuild needed) |
||||
|
├── __init__.py # Package entry point, imports _pyngspice C++ extension |
||||
|
├── runner.py # SpiceRunner interface + NgspiceRunner + SubprocessRunner |
||||
|
├── netlist.py # Netlist pre-processor (Rser= translation for ngspice) |
||||
|
└── _pyngspice.*.pyd # Compiled C++ extension (built in-place, rebuild required) |
||||
|
|
||||
|
src/cpp/ # C++ wrapper code (rebuild required after changes) |
||||
|
├── simulator.cpp/h # Low-level ngspice API wrapper (ngSpice_Init, etc.) |
||||
|
├── callbacks.cpp/h # Callback routing for ngspice events |
||||
|
├── sim_runner.cpp/h # PyLTSpice-compatible simulation runner |
||||
|
├── raw_read.cpp/h # .raw file parser (binary + ASCII, complex data, step detection) |
||||
|
└── trace.cpp/h # Vector/trace handling (numpy array conversion) |
||||
|
|
||||
|
src/bindings/ # pybind11 bindings (rebuild required after changes) |
||||
|
└── module.cpp # PYBIND11_MODULE(_pyngspice, m) — the C++/Python bridge |
||||
|
``` |
||||
|
|
||||
|
### Key Files |
||||
|
| File | Purpose | |
||||
|
|------|---------| |
||||
|
| `CMakeLists.txt` | Main build: compiles ngspice_core static lib + _pyngspice extension | |
||||
|
| `pyproject.toml` | scikit-build-core config, package metadata | |
||||
|
| `build_mingw.py` | One-command build script (sets PATH, syncs venvs) | |
||||
|
| `src/bindings/module.cpp` | pybind11 module definition — all C++/Python type bindings | |
||||
|
| `src/cpp/sim_runner.cpp` | C++ SimRunner — core simulation engine that NgspiceRunner wraps | |
||||
|
| `src/cpp/raw_read.cpp` | .raw file parser — handles both binary and ASCII formats | |
||||
|
| `pyngspice/runner.py` | SpiceRunner interface — what pyTesla consumes | |
||||
|
| `pyngspice/netlist.py` | Rser= pre-processor — critical for LTspice netlist compatibility | |
||||
|
| `visualc/src/include/ngspice/config.h` | Build feature flags (XSPICE, OSDI, CIDER, KLU) | |
||||
|
|
||||
|
## pyTesla Integration |
||||
|
|
||||
|
### SpiceRunner Interface (pyTesla's swap point) |
||||
|
```python |
||||
|
from pyngspice import NgspiceRunner |
||||
|
|
||||
|
# Create runner — replaces PyLTSpice's get_configured_sim_runner() |
||||
|
runner = NgspiceRunner(working_directory="./output") |
||||
|
|
||||
|
# Run simulation — handles Rser= translation automatically |
||||
|
raw_file, log_file = runner.run("tesla_coil.net") |
||||
|
|
||||
|
# Parse results — PyLTSpice's RawRead also works on ngspice .raw files |
||||
|
from pyngspice import RawRead |
||||
|
raw = RawRead(raw_file) |
||||
|
trace = raw.get_trace("V(out)") |
||||
|
data = trace.get_wave(0) # numpy array |
||||
|
``` |
||||
|
|
||||
|
### Netlist Pre-processing |
||||
|
pyngspice automatically translates LTspice syntax to ngspice before simulation: |
||||
|
- `L1 p1 0 8.5u Rser=0.012` → `L1 p1 _rser_L1 8.5u` + `R_L1_ser _rser_L1 0 0.012` |
||||
|
- `.backanno` directives are stripped (LTspice-specific, benign) |
||||
|
- `Rpar=` and `Cpar=` on inductors are stripped (could expand to parallel elements later) |
||||
|
- Standard SPICE constructs (behavioral sources, subcircuits, etc.) pass through unchanged |
||||
|
|
||||
|
### Auto-detection Factory |
||||
|
```python |
||||
|
from pyngspice.runner import get_runner |
||||
|
|
||||
|
# "auto" tries embedded C++ first, falls back to subprocess |
||||
|
runner = get_runner("./output", backend="auto") # or "embedded" or "subprocess" |
||||
|
``` |
||||
|
|
||||
|
## Build Configuration |
||||
|
|
||||
|
### Enabled Features |
||||
|
- **XSPICE** - Code model support (ON by default) |
||||
|
- **OSDI** - Verilog-A support (ON by default) |
||||
|
- **HICUM2** - High-current BJT model with cppduals autodiff library |
||||
|
|
||||
|
### Disabled Features |
||||
|
- **CIDER** - Numerical device models (requires complex KLU setup) |
||||
|
- **KLU** - Sparse matrix solver (requires SuiteSparse build) |
||||
|
|
||||
|
To re-enable, modify `CMakeLists.txt`: |
||||
|
```cmake |
||||
|
option(ENABLE_CIDER "Enable CIDER numerical device models" ON) |
||||
|
option(ENABLE_KLU "Enable KLU sparse matrix solver" ON) |
||||
|
``` |
||||
|
And ensure `visualc/src/include/ngspice/config.h` matches (e.g., `#define CIDER`). |
||||
|
|
||||
|
## Testing |
||||
|
|
||||
|
```bash |
||||
|
pytest tests/ -v # All tests |
||||
|
pytest tests/test_netlist.py -v # Netlist pre-processor only (no build needed) |
||||
|
pytest tests/test_runner.py tests/test_sim.py -v # Integration tests (need built extension) |
||||
|
``` |
||||
|
|
||||
|
### Requirements |
||||
|
- **Every new feature or bug fix must include tests.** Add test methods to the |
||||
|
relevant test class in `tests/`, or create a new class if the feature is distinct. |
||||
|
- **Always run the full test suite** (`pytest tests/test_netlist.py -v` at minimum) |
||||
|
after changes to verify nothing is broken (regression testing). |
||||
|
- Do not consider a change complete until all tests pass. |
||||
|
|
||||
|
## Troubleshooting |
||||
|
|
||||
|
### "DLL load failed" or "ImportError: _pyngspice" |
||||
|
Re-run `python build_mingw.py --clean`. If that doesn't work, check that MinGW-w64 |
||||
|
is at `C:\mingw64\bin` and that `_pyngspice.*.pyd` exists in `pyngspice/`. |
||||
|
|
||||
|
### "No module named pyngspice" |
||||
|
Run `pip install -e .` or `python build_mingw.py` from the repo root. |
||||
|
|
||||
|
### "gcc not found" during build |
||||
|
Install MinGW-w64 to `C:\mingw64`. The build script overrides PATH to avoid Git's |
||||
|
built-in MSYS tools which conflict with MinGW. Run from cmd.exe, not Git Bash. |
||||
|
|
||||
|
### "undefined reference to SIMinfo" |
||||
|
`ngspice.c` was excluded from the build. It contains `SIMinfo` but has no `main()`. |
||||
|
Ensure it's NOT in the exclusion list in `CMakeLists.txt`. |
||||
|
|
||||
|
### "undefined reference to get_nbjt_info, get_numd_info..." |
||||
|
CIDER is enabled in `config.h` but CIDER sources are not compiled. |
||||
|
Ensure `config.h` has `/* #undef CIDER */` when ENABLE_CIDER=OFF. |
||||
|
|
||||
|
### "undefined reference to HICUMload, HICUMtemp" |
||||
|
HICUM2 init is compiled but .cpp implementation files are excluded. |
||||
|
Ensure HICUM2 .cpp files are in the source list and `cppduals` include path is set. |
||||
|
|
||||
|
### Module not found after editable install |
||||
|
The .pyd was not built into `pyngspice/`. Check that `LIBRARY_OUTPUT_DIRECTORY` is |
||||
|
set in CMakeLists.txt. Rebuild or manually copy: |
||||
|
```bash |
||||
|
copy build\cp311-cp311-win_amd64\_pyngspice.cp311-win_amd64.pyd pyngspice\ |
||||
|
``` |
||||
|
|
||||
|
## DO NOT MODIFY (hard-won lessons) |
||||
|
|
||||
|
### ngspice.c must stay in the build |
||||
|
`src/ngspice.c` is NOT the main entry point — it contains `SIMinfo` (device info table). |
||||
|
`src/main.c` is the actual entry point and IS excluded. Do not confuse the two. |
||||
|
|
||||
|
### config.h feature flags must match CMakeLists.txt |
||||
|
`visualc/src/include/ngspice/config.h` and CMakeLists.txt options must agree. |
||||
|
If config.h has `#define CIDER` but ENABLE_CIDER=OFF, you get linker errors for |
||||
|
`get_nbjt_info` etc. If config.h undefs CIDER but ENABLE_CIDER=ON, CIDER sources |
||||
|
compile but the device models aren't registered. |
||||
|
|
||||
|
### The Rser= regex in netlist.py handles the common case |
||||
|
The inductor Rser= pattern handles: bare numbers, engineering suffixes (u, m, n, p, k), |
||||
|
and scientific notation. It does NOT handle parameter references like `Rser={Rprimary}` |
||||
|
(curly brace expressions). If pyTesla generates parameterized Rser values, the pre-processor |
||||
|
will need to be extended to also expand `.param` definitions. |
||||
|
|
||||
|
### MinGW static linking is intentional |
||||
|
We statically link libgcc, libstdc++, and libwinpthread so the .pyd ships zero DLLs. |
||||
|
Without this, users would need `libgcc_s_seh-1.dll`, `libstdc++-6.dll`, and |
||||
|
`libwinpthread-1.dll` in their PATH. Do not remove the static link flags. |
||||
|
|
||||
|
### Raw file format differences |
||||
|
Both LTspice and ngspice produce .raw files, but there are subtle differences: |
||||
|
- NGspice may use different variable naming conventions (lowercase vs mixed case) |
||||
|
- Step detection works via scale vector value resets (handled in raw_read.cpp) |
||||
|
- PyLTSpice's RawRead CAN parse ngspice .raw files (community-verified) |
||||
|
|
||||
|
## Reference Implementation |
||||
|
|
||||
|
- **Authoritative ngspice source**: This repo (`C:\git\ngspice`) tracks upstream ngspice |
||||
|
- **ngspice docs**: https://ngspice.sourceforge.io/docs.html |
||||
|
- **Shared library API**: `src/include/ngspice/sharedspice.h` — the C API we wrap |
||||
|
- **pyfemm-xplat** (sister project): Follow its architecture patterns for build system, |
||||
|
pybind11 bindings, and Python package structure |
||||
|
|
||||
|
## Development Notes |
||||
|
|
||||
|
### Adding New Python Bindings |
||||
|
1. Add C++ wrapper function/class in `src/cpp/` |
||||
|
2. Register with pybind11 in `src/bindings/module.cpp` |
||||
|
3. Export from `pyngspice/__init__.py` if part of public API |
||||
|
|
||||
|
### Adding Netlist Translations |
||||
|
Add new regex patterns or transformations in `pyngspice/netlist.py`. |
||||
|
Add corresponding tests in `tests/test_netlist.py`. |
||||
|
|
||||
|
### Syncing to pyTesla's venv |
||||
|
Edit `SYNC_VENVS` in `build_mingw.py` to include pyTesla's venv path, then: |
||||
|
```bash |
||||
|
python build_mingw.py --sync |
||||
|
``` |
||||
|
This writes a `.pth` file into that venv's site-packages so `import pyngspice` works there. |
||||
@ -0,0 +1,436 @@ |
|||||
|
cmake_minimum_required(VERSION 3.18) |
||||
|
project(pyngspice VERSION 43.0.0 LANGUAGES C CXX) |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Options |
||||
|
# ============================================================================ |
||||
|
option(BUILD_PYTHON_BINDINGS "Build Python bindings" ON) |
||||
|
option(ENABLE_XSPICE "Enable XSPICE code models" ON) |
||||
|
option(ENABLE_OSDI "Enable OSDI (Verilog-A) support" ON) |
||||
|
option(ENABLE_CIDER "Enable CIDER numerical device models" OFF) # Disabled - needs special KLU setup |
||||
|
option(ENABLE_KLU "Enable KLU sparse matrix solver" OFF) # Disabled - needs complex build setup |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Compiler settings |
||||
|
# ============================================================================ |
||||
|
set(CMAKE_CXX_STANDARD 17) |
||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
||||
|
set(CMAKE_C_STANDARD 11) |
||||
|
set(CMAKE_C_EXTENSIONS ON) # Use gnu11 instead of c11 for better complex support |
||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON) |
||||
|
|
||||
|
# Suppress warnings |
||||
|
if(MSVC) |
||||
|
add_compile_options(/W3 /wd4996 /wd4267 /wd4244 /wd4018 /wd4090 /wd4101 /wd4146) |
||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS) |
||||
|
add_definitions(-DWIN32 -D_WINDOWS) |
||||
|
else() |
||||
|
add_compile_options(-Wall -Wno-unused-variable -Wno-unused-function -Wno-sign-compare -Wno-format) |
||||
|
if(CMAKE_C_COMPILER_ID MATCHES "GNU") |
||||
|
add_compile_options(-Wno-maybe-uninitialized -Wno-unused-but-set-variable) |
||||
|
endif() |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Platform detection |
||||
|
# ============================================================================ |
||||
|
if(WIN32) |
||||
|
set(PLATFORM_WINDOWS TRUE) |
||||
|
elseif(APPLE) |
||||
|
set(PLATFORM_MACOS TRUE) |
||||
|
elseif(UNIX) |
||||
|
set(PLATFORM_LINUX TRUE) |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Find dependencies |
||||
|
# ============================================================================ |
||||
|
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module NumPy) |
||||
|
|
||||
|
# Find pybind11 — try system install first, then fall back to pip-installed |
||||
|
find_package(pybind11 CONFIG QUIET) |
||||
|
if(NOT pybind11_FOUND) |
||||
|
# pybind11 is a build dependency in pyproject.toml, so pip has already |
||||
|
# installed it into the build environment. Find it via the Python package. |
||||
|
execute_process( |
||||
|
COMMAND ${Python3_EXECUTABLE} -c |
||||
|
"import pybind11; print(pybind11.get_cmake_dir())" |
||||
|
OUTPUT_VARIABLE PYBIND11_CMAKE_DIR |
||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE |
||||
|
RESULT_VARIABLE PYBIND11_RESULT |
||||
|
) |
||||
|
if(PYBIND11_RESULT EQUAL 0) |
||||
|
list(APPEND CMAKE_PREFIX_PATH ${PYBIND11_CMAKE_DIR}) |
||||
|
find_package(pybind11 CONFIG REQUIRED) |
||||
|
else() |
||||
|
message(FATAL_ERROR |
||||
|
"pybind11 not found. Install it with: pip install pybind11>=2.11") |
||||
|
endif() |
||||
|
endif() |
||||
|
|
||||
|
# Math library on Unix |
||||
|
if(UNIX AND NOT APPLE) |
||||
|
find_library(M_LIBRARY m REQUIRED) |
||||
|
endif() |
||||
|
|
||||
|
find_package(Threads REQUIRED) |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Source directories |
||||
|
# ============================================================================ |
||||
|
set(NGSPICE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Include directories |
||||
|
# ============================================================================ |
||||
|
set(NGSPICE_INCLUDE_DIRS |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/visualc/src/include # Windows config.h |
||||
|
${NGSPICE_SRC} |
||||
|
${NGSPICE_SRC}/include |
||||
|
${NGSPICE_SRC}/include/cppduals # For HICUM2 duals library |
||||
|
${NGSPICE_SRC}/include/ngspice |
||||
|
${NGSPICE_SRC}/spicelib/devices |
||||
|
${NGSPICE_SRC}/spicelib/analysis |
||||
|
${NGSPICE_SRC}/spicelib/parser |
||||
|
${NGSPICE_SRC}/maths/dense |
||||
|
${NGSPICE_SRC}/maths/sparse |
||||
|
${NGSPICE_SRC}/maths/ni |
||||
|
${NGSPICE_SRC}/maths/deriv |
||||
|
${NGSPICE_SRC}/maths/cmaths |
||||
|
${NGSPICE_SRC}/maths/misc |
||||
|
${NGSPICE_SRC}/maths/poly |
||||
|
${NGSPICE_SRC}/maths/fft |
||||
|
${NGSPICE_SRC}/frontend |
||||
|
${NGSPICE_SRC}/frontend/plotting |
||||
|
${NGSPICE_SRC}/frontend/parser |
||||
|
${NGSPICE_SRC}/frontend/help |
||||
|
${NGSPICE_SRC}/frontend/numparam |
||||
|
${NGSPICE_SRC}/frontend/trannoise |
||||
|
${NGSPICE_SRC}/misc |
||||
|
${NGSPICE_SRC}/ciderlib/input |
||||
|
${NGSPICE_SRC}/ciderlib/support |
||||
|
${NGSPICE_SRC}/ciderlib/oned |
||||
|
${NGSPICE_SRC}/ciderlib/twod |
||||
|
${NGSPICE_SRC}/xspice/cm |
||||
|
${NGSPICE_SRC}/xspice/cmpp |
||||
|
${NGSPICE_SRC}/xspice/evt |
||||
|
${NGSPICE_SRC}/xspice/enh |
||||
|
${NGSPICE_SRC}/xspice/ipc |
||||
|
${NGSPICE_SRC}/xspice/idn |
||||
|
${NGSPICE_SRC}/xspice/mif |
||||
|
${NGSPICE_SRC}/xspice/icm |
||||
|
${NGSPICE_SRC}/osdi |
||||
|
) |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Build date/version info |
||||
|
# ============================================================================ |
||||
|
string(TIMESTAMP NGSPICE_BUILD_DATE "%Y-%m-%d") |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Compile definitions |
||||
|
# ============================================================================ |
||||
|
set(NGSPICE_DEFINITIONS |
||||
|
-DNGSPICEDLL |
||||
|
-DSHARED_MODULE |
||||
|
-DSIMULATOR |
||||
|
-DHAS_PROGREP |
||||
|
-DNG_SHARED_BUILD |
||||
|
-DNGSPICEBUILDDATE="${NGSPICE_BUILD_DATE}" |
||||
|
) |
||||
|
|
||||
|
# MinGW-specific: nothing special needed now since we patched cmath1.c |
||||
|
|
||||
|
if(ENABLE_XSPICE) |
||||
|
list(APPEND NGSPICE_DEFINITIONS -DXSPICE) |
||||
|
endif() |
||||
|
|
||||
|
if(ENABLE_OSDI) |
||||
|
list(APPEND NGSPICE_DEFINITIONS -DOSDI) |
||||
|
endif() |
||||
|
|
||||
|
if(ENABLE_CIDER) |
||||
|
list(APPEND NGSPICE_DEFINITIONS -DCIDER) |
||||
|
endif() |
||||
|
|
||||
|
if(ENABLE_KLU) |
||||
|
list(APPEND NGSPICE_DEFINITIONS -DKLU) |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Collect ngspice source files |
||||
|
# ============================================================================ |
||||
|
|
||||
|
# Frontend sources |
||||
|
file(GLOB FRONTEND_SOURCES |
||||
|
${NGSPICE_SRC}/frontend/*.c |
||||
|
) |
||||
|
|
||||
|
# Frontend subdirectories |
||||
|
file(GLOB FRONTEND_PARSER_SOURCES ${NGSPICE_SRC}/frontend/parser/*.c) |
||||
|
file(GLOB FRONTEND_NUMPARAM_SOURCES ${NGSPICE_SRC}/frontend/numparam/*.c) |
||||
|
file(GLOB FRONTEND_TRANNOISE_SOURCES ${NGSPICE_SRC}/frontend/trannoise/*.c) |
||||
|
file(GLOB FRONTEND_HELP_SOURCES ${NGSPICE_SRC}/frontend/help/*.c) |
||||
|
file(GLOB FRONTEND_PLOTTING_SOURCES ${NGSPICE_SRC}/frontend/plotting/*.c) |
||||
|
|
||||
|
# Math sources |
||||
|
file(GLOB MATHS_SPARSE_SOURCES ${NGSPICE_SRC}/maths/sparse/*.c) |
||||
|
file(GLOB MATHS_DENSE_SOURCES ${NGSPICE_SRC}/maths/dense/*.c) |
||||
|
file(GLOB MATHS_NI_SOURCES ${NGSPICE_SRC}/maths/ni/*.c) |
||||
|
file(GLOB MATHS_DERIV_SOURCES ${NGSPICE_SRC}/maths/deriv/*.c) |
||||
|
file(GLOB MATHS_CMATHS_SOURCES ${NGSPICE_SRC}/maths/cmaths/*.c) |
||||
|
file(GLOB MATHS_MISC_SOURCES ${NGSPICE_SRC}/maths/misc/*.c) |
||||
|
file(GLOB MATHS_POLY_SOURCES ${NGSPICE_SRC}/maths/poly/*.c) |
||||
|
file(GLOB MATHS_FFT_SOURCES ${NGSPICE_SRC}/maths/fft/*.c) |
||||
|
|
||||
|
# Spicelib sources |
||||
|
file(GLOB SPICELIB_ANALYSIS_SOURCES ${NGSPICE_SRC}/spicelib/analysis/*.c) |
||||
|
file(GLOB SPICELIB_PARSER_SOURCES ${NGSPICE_SRC}/spicelib/parser/*.c) |
||||
|
|
||||
|
# Device sources - collect all device model subdirectories |
||||
|
file(GLOB_RECURSE SPICELIB_DEVICES_C_SOURCES ${NGSPICE_SRC}/spicelib/devices/*.c) |
||||
|
# HICUM2 .cpp files use the "duals" library for automatic differentiation (header-only, included in ngspice) |
||||
|
file(GLOB_RECURSE SPICELIB_DEVICES_CPP_SOURCES ${NGSPICE_SRC}/spicelib/devices/*.cpp) |
||||
|
set(SPICELIB_DEVICES_SOURCES ${SPICELIB_DEVICES_C_SOURCES} ${SPICELIB_DEVICES_CPP_SOURCES}) |
||||
|
# Exclude KLU-specific binding files when KLU is disabled |
||||
|
if(NOT ENABLE_KLU) |
||||
|
list(FILTER SPICELIB_DEVICES_SOURCES EXCLUDE REGEX "bindCSC\\.c$") |
||||
|
endif() |
||||
|
|
||||
|
# Misc sources |
||||
|
file(GLOB MISC_SOURCES ${NGSPICE_SRC}/misc/*.c) |
||||
|
|
||||
|
# CIDER sources (if enabled) |
||||
|
if(ENABLE_CIDER) |
||||
|
file(GLOB CIDER_INPUT_SOURCES ${NGSPICE_SRC}/ciderlib/input/*.c) |
||||
|
file(GLOB CIDER_SUPPORT_SOURCES ${NGSPICE_SRC}/ciderlib/support/*.c) |
||||
|
file(GLOB CIDER_ONED_SOURCES ${NGSPICE_SRC}/ciderlib/oned/*.c) |
||||
|
file(GLOB CIDER_TWOD_SOURCES ${NGSPICE_SRC}/ciderlib/twod/*.c) |
||||
|
set(CIDER_SOURCES |
||||
|
${CIDER_INPUT_SOURCES} |
||||
|
${CIDER_SUPPORT_SOURCES} |
||||
|
${CIDER_ONED_SOURCES} |
||||
|
${CIDER_TWOD_SOURCES} |
||||
|
) |
||||
|
endif() |
||||
|
|
||||
|
# XSPICE sources (if enabled) |
||||
|
if(ENABLE_XSPICE) |
||||
|
file(GLOB XSPICE_CM_SOURCES ${NGSPICE_SRC}/xspice/cm/*.c) |
||||
|
file(GLOB XSPICE_EVT_SOURCES ${NGSPICE_SRC}/xspice/evt/*.c) |
||||
|
file(GLOB XSPICE_ENH_SOURCES ${NGSPICE_SRC}/xspice/enh/*.c) |
||||
|
file(GLOB XSPICE_IPC_SOURCES ${NGSPICE_SRC}/xspice/ipc/*.c) |
||||
|
file(GLOB XSPICE_IDN_SOURCES ${NGSPICE_SRC}/xspice/idn/*.c) |
||||
|
file(GLOB XSPICE_MIF_SOURCES ${NGSPICE_SRC}/xspice/mif/*.c) |
||||
|
# ICM (code models) built separately |
||||
|
set(XSPICE_SOURCES |
||||
|
${XSPICE_CM_SOURCES} |
||||
|
${XSPICE_EVT_SOURCES} |
||||
|
${XSPICE_ENH_SOURCES} |
||||
|
${XSPICE_IPC_SOURCES} |
||||
|
${XSPICE_IDN_SOURCES} |
||||
|
${XSPICE_MIF_SOURCES} |
||||
|
) |
||||
|
# Exclude KLU-specific binding files when KLU is disabled |
||||
|
if(NOT ENABLE_KLU) |
||||
|
list(FILTER XSPICE_SOURCES EXCLUDE REGEX "bindCSC\\.c$") |
||||
|
endif() |
||||
|
endif() |
||||
|
|
||||
|
# OSDI sources (if enabled) |
||||
|
if(ENABLE_OSDI) |
||||
|
file(GLOB OSDI_SOURCES ${NGSPICE_SRC}/osdi/*.c) |
||||
|
endif() |
||||
|
|
||||
|
# KLU sources (if enabled) |
||||
|
if(ENABLE_KLU) |
||||
|
# KLU sources are in src/maths/KLU directory |
||||
|
file(GLOB KLU_SOURCES ${NGSPICE_SRC}/maths/KLU/*.c) |
||||
|
list(APPEND NGSPICE_INCLUDE_DIRS ${NGSPICE_SRC}/maths/KLU) |
||||
|
endif() |
||||
|
|
||||
|
# Main ngspice files |
||||
|
set(NGSPICE_MAIN_SOURCES |
||||
|
${NGSPICE_SRC}/sharedspice.c |
||||
|
${NGSPICE_SRC}/conf.c |
||||
|
${NGSPICE_SRC}/ngspice.c # Contains SIMinfo definition |
||||
|
) |
||||
|
|
||||
|
# MSVC compatibility |
||||
|
if(MSVC AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/visualc/src/msvc-compat.c) |
||||
|
list(APPEND NGSPICE_MAIN_SOURCES |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/visualc/src/msvc-compat.c |
||||
|
) |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Combine all ngspice sources |
||||
|
# ============================================================================ |
||||
|
set(ALL_NGSPICE_SOURCES |
||||
|
${NGSPICE_MAIN_SOURCES} |
||||
|
${FRONTEND_SOURCES} |
||||
|
${FRONTEND_PARSER_SOURCES} |
||||
|
${FRONTEND_NUMPARAM_SOURCES} |
||||
|
${FRONTEND_TRANNOISE_SOURCES} |
||||
|
${FRONTEND_HELP_SOURCES} |
||||
|
${FRONTEND_PLOTTING_SOURCES} |
||||
|
${MATHS_SPARSE_SOURCES} |
||||
|
${MATHS_DENSE_SOURCES} |
||||
|
${MATHS_NI_SOURCES} |
||||
|
${MATHS_DERIV_SOURCES} |
||||
|
${MATHS_CMATHS_SOURCES} |
||||
|
${MATHS_MISC_SOURCES} |
||||
|
${MATHS_POLY_SOURCES} |
||||
|
${MATHS_FFT_SOURCES} |
||||
|
${SPICELIB_ANALYSIS_SOURCES} |
||||
|
${SPICELIB_PARSER_SOURCES} |
||||
|
${SPICELIB_DEVICES_SOURCES} |
||||
|
${MISC_SOURCES} |
||||
|
) |
||||
|
|
||||
|
if(ENABLE_CIDER) |
||||
|
list(APPEND ALL_NGSPICE_SOURCES ${CIDER_SOURCES}) |
||||
|
endif() |
||||
|
|
||||
|
if(ENABLE_XSPICE) |
||||
|
list(APPEND ALL_NGSPICE_SOURCES ${XSPICE_SOURCES}) |
||||
|
endif() |
||||
|
|
||||
|
if(ENABLE_OSDI) |
||||
|
list(APPEND ALL_NGSPICE_SOURCES ${OSDI_SOURCES}) |
||||
|
endif() |
||||
|
|
||||
|
if(ENABLE_KLU AND DEFINED KLU_SOURCES) |
||||
|
list(APPEND ALL_NGSPICE_SOURCES ${KLU_SOURCES}) |
||||
|
endif() |
||||
|
|
||||
|
# Remove files that cause conflicts or aren't needed for shared library |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "main\\.c$") |
||||
|
# Note: ngspice.c is INCLUDED - it contains SIMinfo but no main() |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "winmain\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "tclspice\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngnutmeg\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "nghelp\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngsconvert\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngmultidec\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "ngproc2mod\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "makeidx\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "hist_info\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "testcommands\\.c$") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "test_accuracy\\.c$") |
||||
|
# Exclude ndev (network device) - uses Unix socket headers |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/ndev/") |
||||
|
# Exclude CIDER-dependent numerical device models when CIDER is disabled |
||||
|
if(NOT ENABLE_CIDER) |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/nbjt/") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/nbjt2/") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/numd/") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/numd2/") |
||||
|
list(FILTER ALL_NGSPICE_SOURCES EXCLUDE REGEX "/numos/") |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Create ngspice static library |
||||
|
# ============================================================================ |
||||
|
add_library(ngspice_core STATIC ${ALL_NGSPICE_SOURCES}) |
||||
|
|
||||
|
target_include_directories(ngspice_core PUBLIC ${NGSPICE_INCLUDE_DIRS}) |
||||
|
target_compile_definitions(ngspice_core PUBLIC ${NGSPICE_DEFINITIONS}) |
||||
|
|
||||
|
if(UNIX AND NOT APPLE) |
||||
|
target_link_libraries(ngspice_core PUBLIC ${M_LIBRARY}) |
||||
|
endif() |
||||
|
|
||||
|
target_link_libraries(ngspice_core PUBLIC Threads::Threads) |
||||
|
|
||||
|
# Windows-specific libraries |
||||
|
if(WIN32) |
||||
|
target_link_libraries(ngspice_core PUBLIC shlwapi) |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Python extension sources |
||||
|
# ============================================================================ |
||||
|
set(PYTHON_EXT_SOURCES |
||||
|
${NGSPICE_SRC}/bindings/module.cpp |
||||
|
${NGSPICE_SRC}/cpp/simulator.cpp |
||||
|
${NGSPICE_SRC}/cpp/callbacks.cpp |
||||
|
${NGSPICE_SRC}/cpp/sim_runner.cpp |
||||
|
${NGSPICE_SRC}/cpp/raw_read.cpp |
||||
|
${NGSPICE_SRC}/cpp/trace.cpp |
||||
|
) |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Create Python module |
||||
|
# ============================================================================ |
||||
|
pybind11_add_module(_pyngspice MODULE ${PYTHON_EXT_SOURCES}) |
||||
|
|
||||
|
target_include_directories(_pyngspice PRIVATE |
||||
|
${NGSPICE_INCLUDE_DIRS} |
||||
|
${NGSPICE_SRC}/cpp |
||||
|
${Python3_INCLUDE_DIRS} |
||||
|
${Python3_NumPy_INCLUDE_DIRS} |
||||
|
) |
||||
|
|
||||
|
target_compile_definitions(_pyngspice PRIVATE |
||||
|
NGSPICE_PYTHON_MODULE |
||||
|
VERSION_STR="${PROJECT_VERSION}" |
||||
|
${NGSPICE_DEFINITIONS} |
||||
|
) |
||||
|
|
||||
|
# Link against ngspice core and Python |
||||
|
target_link_libraries(_pyngspice PRIVATE |
||||
|
ngspice_core |
||||
|
Python3::NumPy |
||||
|
) |
||||
|
|
||||
|
if(APPLE) |
||||
|
target_link_libraries(_pyngspice PRIVATE "-framework Accelerate") |
||||
|
endif() |
||||
|
|
||||
|
# Output .pyd directly into pyngspice/ for inplace editable install |
||||
|
set_target_properties(_pyngspice PROPERTIES |
||||
|
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/pyngspice |
||||
|
) |
||||
|
|
||||
|
# MinGW: statically link all runtime DLLs (ship zero DLLs) |
||||
|
if(MINGW) |
||||
|
target_link_options(_pyngspice PRIVATE -static-libgcc -static-libstdc++) |
||||
|
target_link_libraries(_pyngspice PRIVATE |
||||
|
-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive,-Bdynamic |
||||
|
) |
||||
|
endif() |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Install (for wheel builds) |
||||
|
# ============================================================================ |
||||
|
install(TARGETS _pyngspice |
||||
|
LIBRARY DESTINATION pyngspice |
||||
|
RUNTIME DESTINATION pyngspice |
||||
|
) |
||||
|
|
||||
|
install(FILES |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/pyngspice/__init__.py |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/pyngspice/runner.py |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/pyngspice/netlist.py |
||||
|
DESTINATION pyngspice |
||||
|
) |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Summary |
||||
|
# ============================================================================ |
||||
|
message(STATUS "") |
||||
|
message(STATUS "========================================") |
||||
|
message(STATUS "pyngspice Configuration") |
||||
|
message(STATUS "========================================") |
||||
|
message(STATUS " Version: ${PROJECT_VERSION}") |
||||
|
message(STATUS " Python: ${Python3_EXECUTABLE}") |
||||
|
message(STATUS " NumPy: ${Python3_NumPy_VERSION}") |
||||
|
message(STATUS " pybind11: ${pybind11_VERSION}") |
||||
|
message(STATUS " XSPICE: ${ENABLE_XSPICE}") |
||||
|
message(STATUS " OSDI: ${ENABLE_OSDI}") |
||||
|
message(STATUS " CIDER: ${ENABLE_CIDER}") |
||||
|
message(STATUS " KLU: ${ENABLE_KLU}") |
||||
|
message(STATUS " Source files: ${CMAKE_CURRENT_LIST_DIR}") |
||||
|
message(STATUS "========================================") |
||||
|
message(STATUS "") |
||||
@ -0,0 +1,210 @@ |
|||||
|
#!/usr/bin/env python |
||||
|
""" |
||||
|
Build script for pyngspice using MinGW-w64. |
||||
|
|
||||
|
Single-command build that handles PATH setup, editable install, |
||||
|
and optional venv synchronization for pyTesla integration. |
||||
|
|
||||
|
Usage: |
||||
|
python build_mingw.py # Build and install (editable) |
||||
|
python build_mingw.py --clean # Clean build directory first |
||||
|
python build_mingw.py --sync # Sync .pth files to other venvs |
||||
|
python build_mingw.py --verify # Just verify the install works |
||||
|
python build_mingw.py --debug # Build with debug logging |
||||
|
|
||||
|
Run from cmd.exe, NOT Git Bash (to avoid MSYS path mangling). |
||||
|
""" |
||||
|
|
||||
|
import argparse |
||||
|
import glob |
||||
|
import os |
||||
|
import shutil |
||||
|
import subprocess |
||||
|
import sys |
||||
|
from pathlib import Path |
||||
|
|
||||
|
# ============================================================================ |
||||
|
# Configuration |
||||
|
# ============================================================================ |
||||
|
|
||||
|
REPO_ROOT = Path(__file__).parent.resolve() |
||||
|
MINGW_BIN = r"C:\mingw64\bin" |
||||
|
CMAKE_BIN = r"C:\Program Files\CMake\bin" |
||||
|
PACKAGE_DIR = REPO_ROOT / "pyngspice" |
||||
|
BUILD_DIR = REPO_ROOT / "build" |
||||
|
|
||||
|
# Other venvs that need access to pyngspice (e.g., pyTesla's venv) |
||||
|
# Add paths here as needed: |
||||
|
SYNC_VENVS = [ |
||||
|
# r"C:\git\pytesla\.venv", |
||||
|
] |
||||
|
|
||||
|
|
||||
|
def get_clean_path(): |
||||
|
"""Build a clean PATH that avoids Git MSYS conflicts.""" |
||||
|
python_dir = Path(sys.executable).parent |
||||
|
python_scripts = python_dir / "Scripts" |
||||
|
|
||||
|
path_parts = [ |
||||
|
str(MINGW_BIN), |
||||
|
str(python_dir), |
||||
|
str(python_scripts), |
||||
|
str(CMAKE_BIN), |
||||
|
r"C:\Windows\System32", |
||||
|
r"C:\Windows", |
||||
|
] |
||||
|
|
||||
|
return ";".join(p for p in path_parts if os.path.isdir(p)) |
||||
|
|
||||
|
|
||||
|
def get_build_env(**extra): |
||||
|
"""Get environment variables for the build.""" |
||||
|
env = os.environ.copy() |
||||
|
env["PATH"] = get_clean_path() |
||||
|
env["CC"] = "gcc" |
||||
|
env["CXX"] = "g++" |
||||
|
env.update(extra) |
||||
|
return env |
||||
|
|
||||
|
|
||||
|
def clean(): |
||||
|
"""Remove build directory and old .pyd files.""" |
||||
|
if BUILD_DIR.exists(): |
||||
|
print(f"Removing {BUILD_DIR}...") |
||||
|
shutil.rmtree(BUILD_DIR, ignore_errors=True) |
||||
|
|
||||
|
# Remove old .pyd files from package directory |
||||
|
for pyd in PACKAGE_DIR.glob("*.pyd"): |
||||
|
print(f"Removing {pyd}...") |
||||
|
pyd.unlink() |
||||
|
|
||||
|
# Uninstall any previous install |
||||
|
subprocess.run( |
||||
|
[sys.executable, "-m", "pip", "uninstall", "-y", "pyngspice"], |
||||
|
capture_output=True, |
||||
|
) |
||||
|
print("Clean complete.") |
||||
|
|
||||
|
|
||||
|
def build(debug=False): |
||||
|
"""Build and install pyngspice in editable mode.""" |
||||
|
env = get_build_env() |
||||
|
|
||||
|
# Verify MinGW is available |
||||
|
gcc = shutil.which("gcc", path=env["PATH"]) |
||||
|
if not gcc: |
||||
|
print(f"ERROR: gcc not found. Ensure MinGW is installed at {MINGW_BIN}") |
||||
|
sys.exit(1) |
||||
|
|
||||
|
result = subprocess.run(["gcc", "--version"], capture_output=True, text=True, env=env) |
||||
|
print(f"Using GCC: {result.stdout.splitlines()[0]}") |
||||
|
|
||||
|
# Build command |
||||
|
cmd = [ |
||||
|
sys.executable, "-m", "pip", "install", |
||||
|
"--no-cache-dir", |
||||
|
"-e", ".", |
||||
|
] |
||||
|
|
||||
|
if debug: |
||||
|
# Pass debug flag via environment (picked up by scikit-build-core) |
||||
|
env["CMAKE_ARGS"] = "-DCMAKE_BUILD_TYPE=Debug" |
||||
|
|
||||
|
print(f"\nBuilding pyngspice...") |
||||
|
print(f" Command: {' '.join(cmd)}") |
||||
|
print(f" Working dir: {REPO_ROOT}") |
||||
|
print() |
||||
|
|
||||
|
result = subprocess.run(cmd, cwd=str(REPO_ROOT), env=env) |
||||
|
if result.returncode != 0: |
||||
|
print("\nBuild FAILED.") |
||||
|
sys.exit(result.returncode) |
||||
|
|
||||
|
print("\nBuild complete!") |
||||
|
|
||||
|
|
||||
|
def sync_venvs(): |
||||
|
"""Write .pth files to other venvs so they can import pyngspice.""" |
||||
|
if not SYNC_VENVS: |
||||
|
print("No venvs configured for sync. Edit SYNC_VENVS in build_mingw.py.") |
||||
|
return |
||||
|
|
||||
|
for venv_path in SYNC_VENVS: |
||||
|
venv = Path(venv_path) |
||||
|
if not venv.exists(): |
||||
|
print(f" SKIP (not found): {venv}") |
||||
|
continue |
||||
|
|
||||
|
# Find site-packages |
||||
|
if sys.platform == "win32": |
||||
|
site_packages = venv / "Lib" / "site-packages" |
||||
|
else: |
||||
|
py_ver = f"python{sys.version_info.major}.{sys.version_info.minor}" |
||||
|
site_packages = venv / "lib" / py_ver / "site-packages" |
||||
|
|
||||
|
if not site_packages.exists(): |
||||
|
print(f" SKIP (no site-packages): {venv}") |
||||
|
continue |
||||
|
|
||||
|
pth_file = site_packages / "pyngspice.pth" |
||||
|
pth_file.write_text(str(REPO_ROOT) + "\n") |
||||
|
print(f" Synced: {pth_file}") |
||||
|
|
||||
|
print("Venv sync complete.") |
||||
|
|
||||
|
|
||||
|
def verify(): |
||||
|
"""Verify the pyngspice installation works.""" |
||||
|
print("\nVerifying pyngspice installation...") |
||||
|
|
||||
|
# Test basic import |
||||
|
result = subprocess.run( |
||||
|
[sys.executable, "-c", ( |
||||
|
"from pyngspice import NgspiceRunner, _cpp_available; " |
||||
|
"print(f' C++ extension: {_cpp_available}'); " |
||||
|
"print(f' NgspiceRunner available: {NgspiceRunner.detect()}'); " |
||||
|
"from pyngspice import __version__; " |
||||
|
"print(f' Version: {__version__}'); " |
||||
|
"print(' OK!')" |
||||
|
)], |
||||
|
cwd=str(REPO_ROOT), |
||||
|
) |
||||
|
|
||||
|
if result.returncode != 0: |
||||
|
print("\nVerification FAILED.") |
||||
|
return False |
||||
|
|
||||
|
return True |
||||
|
|
||||
|
|
||||
|
def main(): |
||||
|
parser = argparse.ArgumentParser(description="Build pyngspice with MinGW-w64") |
||||
|
parser.add_argument("--clean", action="store_true", help="Clean build artifacts first") |
||||
|
parser.add_argument("--sync", action="store_true", help="Sync .pth files to other venvs") |
||||
|
parser.add_argument("--verify", action="store_true", help="Verify installation only") |
||||
|
parser.add_argument("--debug", action="store_true", help="Build with debug configuration") |
||||
|
parser.add_argument("--no-verify", action="store_true", help="Skip post-build verification") |
||||
|
args = parser.parse_args() |
||||
|
|
||||
|
if args.verify: |
||||
|
verify() |
||||
|
return |
||||
|
|
||||
|
if args.clean: |
||||
|
clean() |
||||
|
|
||||
|
if args.sync: |
||||
|
sync_venvs() |
||||
|
return |
||||
|
|
||||
|
build(debug=args.debug) |
||||
|
|
||||
|
if not args.no_verify: |
||||
|
verify() |
||||
|
|
||||
|
if SYNC_VENVS: |
||||
|
sync_venvs() |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
||||
@ -0,0 +1,93 @@ |
|||||
|
""" |
||||
|
AC Analysis Example |
||||
|
|
||||
|
This example demonstrates using the pyngspice Python extension |
||||
|
to perform AC analysis (frequency sweep) on an RC filter. |
||||
|
""" |
||||
|
|
||||
|
try: |
||||
|
from pyngspice import SimRunner, RawRead |
||||
|
except ImportError: |
||||
|
print("pyngspice extension not installed. Run: pip install -e .") |
||||
|
exit(1) |
||||
|
|
||||
|
import numpy as np |
||||
|
import os |
||||
|
|
||||
|
# RC filter netlist with AC analysis |
||||
|
netlist_content = """ |
||||
|
* RC Low-Pass Filter - AC Analysis |
||||
|
* Shows magnitude and phase response |
||||
|
|
||||
|
V1 in 0 AC 1 |
||||
|
R1 in out 1k |
||||
|
C1 out 0 100n |
||||
|
|
||||
|
.ac dec 100 1 100k |
||||
|
.end |
||||
|
""" |
||||
|
|
||||
|
def main(): |
||||
|
output_dir = "./output" |
||||
|
os.makedirs(output_dir, exist_ok=True) |
||||
|
|
||||
|
netlist_file = os.path.join(output_dir, "ac_filter.net") |
||||
|
with open(netlist_file, "w") as f: |
||||
|
f.write(netlist_content) |
||||
|
|
||||
|
print("Running AC analysis...") |
||||
|
runner = SimRunner(output_folder=output_dir) |
||||
|
raw_file, log_file = runner.run_now(netlist_file) |
||||
|
|
||||
|
raw = RawRead(raw_file) |
||||
|
print(f"Analysis type: {raw.analysis_type}") |
||||
|
print(f"Complex data: {raw.is_complex}") |
||||
|
print(f"\nTraces: {raw.get_trace_names()}") |
||||
|
|
||||
|
# Get frequency and output voltage (complex) |
||||
|
freq = raw.get_trace("frequency").get_wave(0) |
||||
|
v_out_complex = raw.get_trace("V(out)").get_wave_complex(0) |
||||
|
|
||||
|
# Calculate magnitude in dB and phase in degrees |
||||
|
magnitude_db = 20 * np.log10(np.abs(v_out_complex)) |
||||
|
phase_deg = np.angle(v_out_complex, deg=True) |
||||
|
|
||||
|
# Find -3dB cutoff frequency |
||||
|
idx_3db = np.argmin(np.abs(magnitude_db - (-3))) |
||||
|
f_cutoff = freq[idx_3db] |
||||
|
|
||||
|
print(f"\n-3dB cutoff frequency: {f_cutoff:.2f} Hz") |
||||
|
print(f"Theoretical: {1/(2*np.pi*1e3*100e-9):.2f} Hz") |
||||
|
|
||||
|
# Plot if matplotlib available |
||||
|
try: |
||||
|
import matplotlib.pyplot as plt |
||||
|
|
||||
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True) |
||||
|
|
||||
|
ax1.semilogx(freq, magnitude_db) |
||||
|
ax1.axhline(-3, color='r', linestyle='--', alpha=0.5, label='-3dB') |
||||
|
ax1.axvline(f_cutoff, color='r', linestyle='--', alpha=0.5) |
||||
|
ax1.set_ylabel('Magnitude (dB)') |
||||
|
ax1.set_title('RC Low-Pass Filter Frequency Response') |
||||
|
ax1.legend() |
||||
|
ax1.grid(True, which='both', alpha=0.3) |
||||
|
|
||||
|
ax2.semilogx(freq, phase_deg) |
||||
|
ax2.axhline(-45, color='r', linestyle='--', alpha=0.5, label='-45°') |
||||
|
ax2.set_xlabel('Frequency (Hz)') |
||||
|
ax2.set_ylabel('Phase (degrees)') |
||||
|
ax2.legend() |
||||
|
ax2.grid(True, which='both', alpha=0.3) |
||||
|
|
||||
|
plt.tight_layout() |
||||
|
plot_file = os.path.join(output_dir, "ac_filter.png") |
||||
|
plt.savefig(plot_file, dpi=150) |
||||
|
print(f"\nPlot saved to: {plot_file}") |
||||
|
plt.show() |
||||
|
|
||||
|
except ImportError: |
||||
|
print("\nInstall matplotlib for plotting: pip install matplotlib") |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
||||
@ -0,0 +1,94 @@ |
|||||
|
""" |
||||
|
Simple RC Circuit Example |
||||
|
|
||||
|
This example demonstrates using the pyngspice Python extension |
||||
|
to simulate a basic RC low-pass filter. |
||||
|
""" |
||||
|
|
||||
|
# Note: This example requires the ngspice extension to be built and installed |
||||
|
# Run: pip install -e . from the ngspice root directory |
||||
|
|
||||
|
try: |
||||
|
from pyngspice import SimRunner, RawRead |
||||
|
except ImportError: |
||||
|
print("pyngspice extension not installed. Run: pip install -e .") |
||||
|
print("This example shows the intended API usage.") |
||||
|
exit(1) |
||||
|
|
||||
|
import numpy as np |
||||
|
import os |
||||
|
|
||||
|
# Create a simple RC netlist |
||||
|
netlist_content = """ |
||||
|
* Simple RC Low-Pass Filter |
||||
|
* Cutoff frequency: ~1.59 kHz |
||||
|
|
||||
|
V1 in 0 AC 1 SIN(0 1 1k) |
||||
|
R1 in out 1k |
||||
|
C1 out 0 100n |
||||
|
|
||||
|
.tran 10u 10m |
||||
|
.end |
||||
|
""" |
||||
|
|
||||
|
def main(): |
||||
|
# Create output directory |
||||
|
output_dir = "./output" |
||||
|
os.makedirs(output_dir, exist_ok=True) |
||||
|
|
||||
|
# Write netlist to file |
||||
|
netlist_file = os.path.join(output_dir, "simple_rc.net") |
||||
|
with open(netlist_file, "w") as f: |
||||
|
f.write(netlist_content) |
||||
|
|
||||
|
print(f"Created netlist: {netlist_file}") |
||||
|
|
||||
|
# Create SimRunner and run simulation |
||||
|
print("Running simulation...") |
||||
|
runner = SimRunner(output_folder=output_dir) |
||||
|
raw_file, log_file = runner.run_now(netlist_file) |
||||
|
|
||||
|
print(f"Raw file: {raw_file}") |
||||
|
print(f"Log file: {log_file}") |
||||
|
|
||||
|
# Read results |
||||
|
raw = RawRead(raw_file) |
||||
|
print(f"\nAnalysis type: {raw.analysis_type}") |
||||
|
print(f"Number of traces: {raw.num_variables}") |
||||
|
print(f"Number of points: {raw.num_points}") |
||||
|
print(f"\nAvailable traces:") |
||||
|
for name in raw.get_trace_names(): |
||||
|
print(f" - {name}") |
||||
|
|
||||
|
# Get time and voltage data |
||||
|
time = raw.get_trace("time").get_wave(0) |
||||
|
v_in = raw.get_trace("V(in)").get_wave(0) |
||||
|
v_out = raw.get_trace("V(out)").get_wave(0) |
||||
|
|
||||
|
print(f"\nTime range: {time[0]*1e3:.3f} ms to {time[-1]*1e3:.3f} ms") |
||||
|
print(f"V(in) range: {min(v_in):.3f} V to {max(v_in):.3f} V") |
||||
|
print(f"V(out) range: {min(v_out):.3f} V to {max(v_out):.3f} V") |
||||
|
|
||||
|
# Optional: Plot results if matplotlib is available |
||||
|
try: |
||||
|
import matplotlib.pyplot as plt |
||||
|
|
||||
|
fig, ax = plt.subplots(figsize=(10, 6)) |
||||
|
ax.plot(time * 1e3, v_in, label='V(in)', alpha=0.7) |
||||
|
ax.plot(time * 1e3, v_out, label='V(out)', linewidth=2) |
||||
|
ax.set_xlabel('Time (ms)') |
||||
|
ax.set_ylabel('Voltage (V)') |
||||
|
ax.set_title('RC Low-Pass Filter Response') |
||||
|
ax.legend() |
||||
|
ax.grid(True, alpha=0.3) |
||||
|
|
||||
|
plot_file = os.path.join(output_dir, "simple_rc.png") |
||||
|
plt.savefig(plot_file, dpi=150) |
||||
|
print(f"\nPlot saved to: {plot_file}") |
||||
|
plt.show() |
||||
|
|
||||
|
except ImportError: |
||||
|
print("\nNote: Install matplotlib to generate plots") |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
||||
@ -0,0 +1,85 @@ |
|||||
|
""" |
||||
|
pyngspice - Python bindings for ngspice circuit simulator. |
||||
|
|
||||
|
Provides embedded ngspice simulation with a SpiceRunner interface |
||||
|
compatible with pyTesla. Can serve as a drop-in alternative to LTspice |
||||
|
for cross-platform Tesla coil simulation. |
||||
|
|
||||
|
Basic usage: |
||||
|
from pyngspice import NgspiceRunner, RawRead |
||||
|
|
||||
|
runner = NgspiceRunner(working_directory="./output") |
||||
|
raw_file, log_file = runner.run("circuit.net") |
||||
|
|
||||
|
raw = RawRead(raw_file) |
||||
|
trace = raw.get_trace("V(out)") |
||||
|
data = trace.get_wave(0) # numpy array |
||||
|
|
||||
|
Low-level usage (PyLTSpice-compatible): |
||||
|
from pyngspice import SimRunner, RawRead |
||||
|
|
||||
|
runner = SimRunner(output_folder="./output") |
||||
|
raw_file, log_file = runner.run_now("circuit.net") |
||||
|
""" |
||||
|
|
||||
|
__version__ = "43.0.0" |
||||
|
__author__ = "ngspice team" |
||||
|
|
||||
|
# On Windows with Python 3.8+, add MinGW DLL directory for dependent DLLs |
||||
|
import sys |
||||
|
import os |
||||
|
if sys.platform == 'win32' and hasattr(os, 'add_dll_directory'): |
||||
|
# MinGW runtime DLLs (libgcc_s_seh-1.dll, libstdc++-6.dll, libwinpthread-1.dll) |
||||
|
mingw_bin = r'C:\mingw64\bin' |
||||
|
if os.path.isdir(mingw_bin): |
||||
|
os.add_dll_directory(mingw_bin) |
||||
|
|
||||
|
# Import the compiled C++ extension module |
||||
|
try: |
||||
|
from ._pyngspice import ( |
||||
|
Simulator, |
||||
|
SimRunner, |
||||
|
RawRead, |
||||
|
Trace, |
||||
|
NgSpice, |
||||
|
) |
||||
|
_cpp_available = True |
||||
|
except ImportError as e: |
||||
|
_cpp_available = False |
||||
|
_cpp_error = str(e) |
||||
|
# Provide helpful error message |
||||
|
import warnings |
||||
|
warnings.warn( |
||||
|
f"Failed to import pyngspice C++ extension: {e}\n" |
||||
|
f"The embedded ngspice runner will not be available.\n" |
||||
|
f"Try: pip install -e . (from the ngspice source directory)", |
||||
|
ImportWarning, |
||||
|
stacklevel=2, |
||||
|
) |
||||
|
# Define stubs so imports don't fail |
||||
|
Simulator = None |
||||
|
SimRunner = None |
||||
|
RawRead = None |
||||
|
Trace = None |
||||
|
NgSpice = None |
||||
|
|
||||
|
# Import Python-level API |
||||
|
from .runner import SpiceRunner, NgspiceRunner, SubprocessRunner, SimulationError |
||||
|
from .netlist import preprocess_netlist |
||||
|
|
||||
|
__all__ = [ |
||||
|
# High-level API (what pyTesla uses) |
||||
|
"SpiceRunner", |
||||
|
"NgspiceRunner", |
||||
|
"SubprocessRunner", |
||||
|
"SimulationError", |
||||
|
"preprocess_netlist", |
||||
|
# Low-level C++ bindings |
||||
|
"Simulator", |
||||
|
"SimRunner", |
||||
|
"RawRead", |
||||
|
"Trace", |
||||
|
"NgSpice", |
||||
|
# State |
||||
|
"_cpp_available", |
||||
|
] |
||||
@ -0,0 +1,518 @@ |
|||||
|
""" |
||||
|
Netlist pre-processor for ngspice compatibility. |
||||
|
|
||||
|
Transforms LTspice-style netlist constructs into ngspice-compatible |
||||
|
equivalents. The primary transformation is converting Rser= parameters |
||||
|
on inductor lines into separate series resistor elements. |
||||
|
|
||||
|
Transformations applied: |
||||
|
- Inductor Rser=: L1 p1 0 8.5u Rser=0.012 -> L1 + R_L1_ser |
||||
|
- Strip .backanno directives (LTspice-specific, benign) |
||||
|
|
||||
|
Usage: |
||||
|
from pyngspice.netlist import preprocess_netlist |
||||
|
|
||||
|
with open("circuit.net") as f: |
||||
|
original = f.read() |
||||
|
|
||||
|
processed = preprocess_netlist(original) |
||||
|
""" |
||||
|
|
||||
|
import re |
||||
|
from typing import List, Tuple |
||||
|
|
||||
|
|
||||
|
def preprocess_netlist(content: str) -> str: |
||||
|
"""Pre-process a SPICE netlist for ngspice compatibility. |
||||
|
|
||||
|
Applies all necessary transformations to convert an LTspice-style |
||||
|
netlist into one that ngspice can parse correctly. |
||||
|
|
||||
|
Args: |
||||
|
content: The raw netlist string (LTspice format) |
||||
|
|
||||
|
Returns: |
||||
|
Processed netlist string ready for ngspice |
||||
|
""" |
||||
|
lines = content.splitlines() |
||||
|
output_lines = [] |
||||
|
extra_components = [] # Series resistors to insert before .end |
||||
|
has_savecurrents = False |
||||
|
targeted_cap_names = set() # Capacitor names from .save i(C_*) directives |
||||
|
|
||||
|
for line in lines: |
||||
|
stripped = line.strip() |
||||
|
|
||||
|
# Skip empty lines (preserve them) |
||||
|
if not stripped: |
||||
|
output_lines.append(line) |
||||
|
continue |
||||
|
|
||||
|
# Strip .backanno directives (LTspice-specific, ignored by ngspice) |
||||
|
if stripped.lower().startswith('.backanno'): |
||||
|
continue |
||||
|
|
||||
|
# Detect and strip .options savecurrents (doesn't work in embedded mode) |
||||
|
if _is_savecurrents_option(stripped): |
||||
|
has_savecurrents = True |
||||
|
continue |
||||
|
|
||||
|
# Handle continuation lines (start with +) — pass through |
||||
|
if stripped.startswith('+'): |
||||
|
output_lines.append(line) |
||||
|
continue |
||||
|
|
||||
|
# Detect .save i(C_*) directives for targeted capacitor probing |
||||
|
if stripped.lower().startswith('.save'): |
||||
|
cap_names = _parse_save_cap_currents(stripped) |
||||
|
if cap_names: |
||||
|
targeted_cap_names.update(name.upper() for name in cap_names) |
||||
|
output_lines.append(line) |
||||
|
continue |
||||
|
|
||||
|
# Handle inductor lines with Rser= parameter |
||||
|
if stripped[0].upper() == 'L' and 'rser' in stripped.lower(): |
||||
|
processed_line, extras = _process_inductor_rser(stripped) |
||||
|
if extras: |
||||
|
output_lines.append(processed_line) |
||||
|
extra_components.extend(extras) |
||||
|
continue |
||||
|
|
||||
|
output_lines.append(line) |
||||
|
|
||||
|
# Insert extra components (series resistors) just before .end |
||||
|
if extra_components: |
||||
|
result = [] |
||||
|
for line in output_lines: |
||||
|
if line.strip().lower() == '.end': |
||||
|
result.append('* --- pyngspice: inductor series resistance expansion ---') |
||||
|
result.extend(extra_components) |
||||
|
result.append(line) |
||||
|
output_lines = result |
||||
|
|
||||
|
# Insert 0V voltage source probes for capacitor current measurement |
||||
|
if has_savecurrents: |
||||
|
output_lines = _insert_capacitor_probes(output_lines) |
||||
|
|
||||
|
# Expand .options savecurrents into explicit .save directives |
||||
|
if has_savecurrents: |
||||
|
output_lines = _expand_savecurrents(output_lines) |
||||
|
|
||||
|
# Targeted capacitor probing from .save i(C_*) directives |
||||
|
# Only active when savecurrents is NOT present (savecurrents probes everything already) |
||||
|
if targeted_cap_names and not has_savecurrents: |
||||
|
output_lines = _insert_targeted_capacitor_probes(output_lines, targeted_cap_names) |
||||
|
output_lines = _rewrite_save_directives(output_lines, targeted_cap_names) |
||||
|
|
||||
|
return '\n'.join(output_lines) |
||||
|
|
||||
|
|
||||
|
# Pattern for inductor lines with Rser= parameter |
||||
|
# Matches: L<name> <node+> <node-> <value> Rser=<value> [other params...] |
||||
|
# Value can be: number, number with suffix (8.5u, 100n, 1.2k), parameter ref ({Lpri}), |
||||
|
# or scientific notation (1.5e-6) |
||||
|
_INDUCTOR_RSER_PATTERN = re.compile( |
||||
|
r'^(L\w+)' # Group 1: Inductor name (L1, Lprimary, etc.) |
||||
|
r'\s+' |
||||
|
r'(\S+)' # Group 2: Positive node |
||||
|
r'\s+' |
||||
|
r'(\S+)' # Group 3: Negative node |
||||
|
r'\s+' |
||||
|
r'(\S+)' # Group 4: Inductance value |
||||
|
r'\s+' |
||||
|
r'Rser\s*=\s*' # Rser= keyword |
||||
|
r'(\S+)' # Group 5: Series resistance value |
||||
|
r'(.*)', # Group 6: Remaining parameters (Rpar=, Cpar=, etc.) |
||||
|
re.IGNORECASE |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def _process_inductor_rser(line: str) -> Tuple[str, List[str]]: |
||||
|
"""Process Rser= parameter on an inductor line. |
||||
|
|
||||
|
Transforms: |
||||
|
L1 p1 0 8.5u Rser=0.012 |
||||
|
Into: |
||||
|
L1 p1 _rser_L1 8.5u |
||||
|
Plus extra line: |
||||
|
R_L1_ser _rser_L1 0 0.012 |
||||
|
|
||||
|
The intermediate node name uses _rser_<name> prefix to avoid |
||||
|
collisions with user-defined node names. |
||||
|
|
||||
|
Args: |
||||
|
line: The inductor line to process |
||||
|
|
||||
|
Returns: |
||||
|
Tuple of (modified_line, list_of_extra_lines). |
||||
|
If no Rser= found, returns (original_line, []). |
||||
|
""" |
||||
|
match = _INDUCTOR_RSER_PATTERN.match(line.strip()) |
||||
|
if not match: |
||||
|
return line, [] |
||||
|
|
||||
|
name = match.group(1) # e.g., L1 |
||||
|
node_p = match.group(2) # e.g., p1 |
||||
|
node_n = match.group(3) # e.g., 0 |
||||
|
inductance = match.group(4) # e.g., 8.5u |
||||
|
rser_val = match.group(5) # e.g., 0.012 |
||||
|
remaining = match.group(6).strip() # e.g., Cpar=10p |
||||
|
|
||||
|
# Create unique intermediate node name |
||||
|
int_node = f"_rser_{name}" |
||||
|
|
||||
|
# Build modified inductor line (inductor connects to intermediate node) |
||||
|
new_inductor = f"{name} {node_p} {int_node} {inductance}" |
||||
|
|
||||
|
# Preserve any remaining parameters (Rpar=, Cpar=, etc.) |
||||
|
# Strip any additional Rser-like params that ngspice doesn't support |
||||
|
if remaining: |
||||
|
# Remove Rpar= and Cpar= as well if present (ngspice doesn't support these either) |
||||
|
cleaned = _strip_ltspice_inductor_params(remaining) |
||||
|
if cleaned: |
||||
|
new_inductor += f" {cleaned}" |
||||
|
|
||||
|
# Build series resistor line |
||||
|
resistor = f"R_{name}_ser {int_node} {node_n} {rser_val}" |
||||
|
|
||||
|
return new_inductor, [resistor] |
||||
|
|
||||
|
|
||||
|
# SPICE component prefixes whose current can be saved with i(name) |
||||
|
# K (coupling) is excluded — it has no "through" current |
||||
|
_COMPONENT_PREFIXES = set('RCLVIDEFJMQBXGHrclvidefjmqbxgh') |
||||
|
|
||||
|
|
||||
|
def _is_savecurrents_option(line: str) -> bool: |
||||
|
"""Check if a line is .options savecurrents (any case, any spacing).""" |
||||
|
lowered = line.strip().lower() |
||||
|
if not lowered.startswith('.options') and not lowered.startswith('.option'): |
||||
|
return False |
||||
|
return 'savecurrents' in lowered |
||||
|
|
||||
|
|
||||
|
# Pattern to extract i(name) references from .save directives |
||||
|
_SAVE_CURRENT_PATTERN = re.compile(r'i\((\w+)\)', re.IGNORECASE) |
||||
|
|
||||
|
|
||||
|
def _parse_save_cap_currents(line: str) -> list: |
||||
|
"""Extract capacitor names from .save i(C_name) directives. |
||||
|
|
||||
|
Parses a .save directive line and returns names of capacitors |
||||
|
whose currents are being saved. Non-capacitor names (V1, R1, L1) |
||||
|
are ignored. |
||||
|
|
||||
|
Args: |
||||
|
line: A .save directive line (e.g., ".save i(C_mmc) i(V1)") |
||||
|
|
||||
|
Returns: |
||||
|
List of capacitor names found (e.g., ["C_mmc"]) |
||||
|
""" |
||||
|
if not line.strip().lower().startswith('.save'): |
||||
|
return [] |
||||
|
return [m.group(1) for m in _SAVE_CURRENT_PATTERN.finditer(line) |
||||
|
if m.group(1)[0].upper() == 'C'] |
||||
|
|
||||
|
|
||||
|
def _expand_savecurrents(lines: List[str]) -> List[str]: |
||||
|
"""Replace .options savecurrents with explicit .save directives. |
||||
|
|
||||
|
Scans the netlist for all component names and generates: |
||||
|
.save all |
||||
|
.save i(V1) i(C1) i(R1) ... |
||||
|
|
||||
|
This is needed because .options savecurrents doesn't work reliably |
||||
|
in ngspice's embedded (shared library) mode — the write command |
||||
|
silently fails to produce a .raw file. |
||||
|
|
||||
|
Args: |
||||
|
lines: Processed netlist lines (Rser already expanded, savecurrents already stripped) |
||||
|
|
||||
|
Returns: |
||||
|
Modified lines with .save directives inserted before .end |
||||
|
""" |
||||
|
component_names = _collect_component_names(lines) |
||||
|
|
||||
|
if not component_names: |
||||
|
return lines |
||||
|
|
||||
|
# Build .save directives |
||||
|
save_lines = [ |
||||
|
'* --- pyngspice: explicit current saves (expanded from .options directive) ---', |
||||
|
'.save all', |
||||
|
] |
||||
|
|
||||
|
# Group i(name) saves into lines of reasonable length |
||||
|
current_saves = [f'i({name})' for name in component_names] |
||||
|
# Put them all on one .save line (ngspice handles long lines fine) |
||||
|
save_lines.append('.save ' + ' '.join(current_saves)) |
||||
|
|
||||
|
# Insert before .end |
||||
|
result = [] |
||||
|
for line in lines: |
||||
|
if line.strip().lower() == '.end': |
||||
|
result.extend(save_lines) |
||||
|
result.append(line) |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
|
||||
|
def _collect_component_names(lines: List[str]) -> List[str]: |
||||
|
"""Extract component names from netlist lines. |
||||
|
|
||||
|
Returns names of components whose current can be saved with i(name). |
||||
|
Skips: comments, directives, continuations, K elements, blank lines. |
||||
|
|
||||
|
Args: |
||||
|
lines: Netlist lines to scan |
||||
|
|
||||
|
Returns: |
||||
|
List of component names in order of appearance |
||||
|
""" |
||||
|
names = [] |
||||
|
for line in lines: |
||||
|
stripped = line.strip() |
||||
|
if not stripped: |
||||
|
continue |
||||
|
first_char = stripped[0] |
||||
|
# Skip comments, directives, continuations |
||||
|
if first_char in ('*', '.', '+'): |
||||
|
continue |
||||
|
# Skip K elements (coupling coefficients) |
||||
|
if first_char in ('K', 'k'): |
||||
|
continue |
||||
|
# Check if it's a component line |
||||
|
if first_char in _COMPONENT_PREFIXES: |
||||
|
# Component name is the first token |
||||
|
tokens = stripped.split() |
||||
|
if tokens: |
||||
|
names.append(tokens[0]) |
||||
|
return names |
||||
|
|
||||
|
|
||||
|
def _insert_capacitor_probes(lines: List[str]) -> List[str]: |
||||
|
"""Insert 0V voltage source probes in series with capacitors. |
||||
|
|
||||
|
ngspice embedded mode silently ignores .save i(capacitor) directives. |
||||
|
The workaround is to insert a 0V voltage source in series with each |
||||
|
capacitor — V-source currents are always saved. The trace is later |
||||
|
renamed from i(v_probe_Cname) back to i(Cname) in raw file post-processing. |
||||
|
|
||||
|
Only processes top-level capacitors (skips those inside .subckt blocks). |
||||
|
|
||||
|
Transforms: |
||||
|
C_mmc vin p1 0.03u |
||||
|
Into: |
||||
|
C_mmc _probe_C_mmc p1 0.03u |
||||
|
Plus extra line before .end: |
||||
|
V_probe_C_mmc vin _probe_C_mmc 0 |
||||
|
|
||||
|
Args: |
||||
|
lines: Processed netlist lines (Rser already expanded, savecurrents stripped) |
||||
|
|
||||
|
Returns: |
||||
|
Modified lines with capacitor probes inserted |
||||
|
""" |
||||
|
modified_lines = [] |
||||
|
probe_lines = [] |
||||
|
subckt_depth = 0 |
||||
|
|
||||
|
for line in lines: |
||||
|
stripped = line.strip() |
||||
|
|
||||
|
# Track .subckt nesting |
||||
|
if stripped.lower().startswith('.subckt'): |
||||
|
subckt_depth += 1 |
||||
|
modified_lines.append(line) |
||||
|
continue |
||||
|
elif stripped.lower().startswith('.ends'): |
||||
|
subckt_depth -= 1 |
||||
|
modified_lines.append(line) |
||||
|
continue |
||||
|
|
||||
|
# Only process top-level capacitors |
||||
|
if subckt_depth == 0 and stripped and stripped[0].upper() == 'C': |
||||
|
mod_line, probe = _process_capacitor_probe(stripped) |
||||
|
if probe: |
||||
|
modified_lines.append(mod_line) |
||||
|
probe_lines.append(probe) |
||||
|
continue |
||||
|
|
||||
|
modified_lines.append(line) |
||||
|
|
||||
|
# Insert probe V sources before .end |
||||
|
if probe_lines: |
||||
|
result = [] |
||||
|
for line in modified_lines: |
||||
|
if line.strip().lower() == '.end': |
||||
|
result.append('* --- pyngspice: capacitor current probes ---') |
||||
|
result.extend(probe_lines) |
||||
|
result.append(line) |
||||
|
return result |
||||
|
|
||||
|
return modified_lines |
||||
|
|
||||
|
|
||||
|
def _insert_targeted_capacitor_probes(lines: List[str], target_caps: set) -> List[str]: |
||||
|
"""Insert 0V voltage source probes for specific capacitors only. |
||||
|
|
||||
|
Like _insert_capacitor_probes(), but only processes capacitors whose |
||||
|
names (case-insensitive) are in target_caps. This avoids the performance |
||||
|
penalty of probing all capacitors when only a few currents are needed. |
||||
|
|
||||
|
Args: |
||||
|
lines: Processed netlist lines |
||||
|
target_caps: Set of capacitor names to probe (uppercase for comparison) |
||||
|
|
||||
|
Returns: |
||||
|
Modified lines with targeted capacitor probes inserted |
||||
|
""" |
||||
|
modified_lines = [] |
||||
|
probe_lines = [] |
||||
|
subckt_depth = 0 |
||||
|
|
||||
|
for line in lines: |
||||
|
stripped = line.strip() |
||||
|
|
||||
|
# Track .subckt nesting |
||||
|
if stripped.lower().startswith('.subckt'): |
||||
|
subckt_depth += 1 |
||||
|
modified_lines.append(line) |
||||
|
continue |
||||
|
elif stripped.lower().startswith('.ends'): |
||||
|
subckt_depth -= 1 |
||||
|
modified_lines.append(line) |
||||
|
continue |
||||
|
|
||||
|
# Only process top-level capacitors that are in the target set |
||||
|
if subckt_depth == 0 and stripped and stripped[0].upper() == 'C': |
||||
|
tokens = stripped.split() |
||||
|
if tokens and tokens[0].upper() in target_caps: |
||||
|
mod_line, probe = _process_capacitor_probe(stripped) |
||||
|
if probe: |
||||
|
modified_lines.append(mod_line) |
||||
|
probe_lines.append(probe) |
||||
|
continue |
||||
|
|
||||
|
modified_lines.append(line) |
||||
|
|
||||
|
# Insert probe V sources before .end |
||||
|
if probe_lines: |
||||
|
result = [] |
||||
|
for line in modified_lines: |
||||
|
if line.strip().lower() == '.end': |
||||
|
result.append('* --- pyngspice: targeted capacitor current probes ---') |
||||
|
result.extend(probe_lines) |
||||
|
result.append(line) |
||||
|
return result |
||||
|
|
||||
|
return modified_lines |
||||
|
|
||||
|
|
||||
|
def _process_capacitor_probe(line: str) -> Tuple[str, str]: |
||||
|
"""Insert a 0V voltage source probe in series with a capacitor. |
||||
|
|
||||
|
Transforms: |
||||
|
C_mmc vin p1 0.03u |
||||
|
Into modified line: |
||||
|
C_mmc _probe_C_mmc p1 0.03u |
||||
|
And probe line: |
||||
|
V_probe_C_mmc vin _probe_C_mmc 0 |
||||
|
|
||||
|
Args: |
||||
|
line: A capacitor line (must start with C) |
||||
|
|
||||
|
Returns: |
||||
|
(modified_cap_line, probe_v_source_line). |
||||
|
If parsing fails, returns (original_line, ""). |
||||
|
""" |
||||
|
tokens = line.strip().split() |
||||
|
if len(tokens) < 4: |
||||
|
return line, "" |
||||
|
|
||||
|
name = tokens[0] # C_mmc |
||||
|
node1 = tokens[1] # vin (positive node) |
||||
|
node2 = tokens[2] # p1 (negative node) |
||||
|
rest = ' '.join(tokens[3:]) # 0.03u [params...] |
||||
|
|
||||
|
int_node = f"_probe_{name}" |
||||
|
modified_cap = f"{name} {int_node} {node2} {rest}" |
||||
|
probe_source = f"V_probe_{name} {node1} {int_node} 0" |
||||
|
|
||||
|
return modified_cap, probe_source |
||||
|
|
||||
|
|
||||
|
def _rewrite_save_directives(lines: List[str], probed_caps: set) -> List[str]: |
||||
|
"""Rewrite .save directives to reference probe V-sources instead of capacitors. |
||||
|
|
||||
|
When targeted capacitor probing is active: |
||||
|
- .save i(C_mmc) -> .save i(V_probe_C_mmc) |
||||
|
- .save i(V1) i(C_mmc) -> .save i(V1) i(V_probe_C_mmc) |
||||
|
- .save v(out) -> .save v(out) (voltages unchanged) |
||||
|
- Ensures .save all is present (needed for voltage traces) |
||||
|
|
||||
|
Args: |
||||
|
lines: Netlist lines (after probe insertion) |
||||
|
probed_caps: Set of capacitor names that were probed (uppercase for matching) |
||||
|
|
||||
|
Returns: |
||||
|
Modified lines with .save directives rewritten |
||||
|
""" |
||||
|
has_save_all = False |
||||
|
result = [] |
||||
|
|
||||
|
for line in lines: |
||||
|
stripped = line.strip() |
||||
|
|
||||
|
if stripped.lower().startswith('.save'): |
||||
|
# Check if this is .save all |
||||
|
if stripped.lower().split() == ['.save', 'all']: |
||||
|
has_save_all = True |
||||
|
result.append(line) |
||||
|
continue |
||||
|
|
||||
|
# Rewrite i(C_name) references to i(V_probe_C_name) |
||||
|
def rewrite_cap_current(match): |
||||
|
name = match.group(1) |
||||
|
if name.upper() in probed_caps: |
||||
|
return f'i(V_probe_{name})' |
||||
|
return match.group(0) |
||||
|
|
||||
|
rewritten = _SAVE_CURRENT_PATTERN.sub(rewrite_cap_current, stripped) |
||||
|
result.append(rewritten) |
||||
|
else: |
||||
|
result.append(line) |
||||
|
|
||||
|
# Ensure .save all is present (user may have only .save i(C_mmc), but |
||||
|
# we still need voltage traces) |
||||
|
if not has_save_all: |
||||
|
final = [] |
||||
|
for line in result: |
||||
|
if line.strip().lower() == '.end': |
||||
|
final.append('* --- pyngspice: auto-inserted .save all for voltage traces ---') |
||||
|
final.append('.save all') |
||||
|
final.append(line) |
||||
|
return final |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
|
||||
|
def _strip_ltspice_inductor_params(params: str) -> str: |
||||
|
"""Remove LTspice-specific inductor parameters that ngspice doesn't support. |
||||
|
|
||||
|
LTspice supports Rser=, Rpar=, Cpar= on inductors. NGspice does not. |
||||
|
These need to be removed or converted to separate components. |
||||
|
|
||||
|
For now, we just strip them. In the future, Rpar= and Cpar= could |
||||
|
be expanded into parallel R and C elements. |
||||
|
|
||||
|
Args: |
||||
|
params: Remaining parameter string after the inductance value |
||||
|
|
||||
|
Returns: |
||||
|
Cleaned parameter string with LTspice-specific params removed |
||||
|
""" |
||||
|
# Remove Rpar=<value> and Cpar=<value> |
||||
|
cleaned = re.sub(r'Rpar\s*=\s*\S+', '', params, flags=re.IGNORECASE) |
||||
|
cleaned = re.sub(r'Cpar\s*=\s*\S+', '', cleaned, flags=re.IGNORECASE) |
||||
|
return cleaned.strip() |
||||
@ -0,0 +1,444 @@ |
|||||
|
""" |
||||
|
SpiceRunner interface and implementations for pyTesla integration. |
||||
|
|
||||
|
Provides a common interface for running SPICE simulations with different |
||||
|
backends (embedded ngspice, subprocess ngspice, etc.). pyTesla uses |
||||
|
this interface to swap between LTspice and ngspice seamlessly. |
||||
|
|
||||
|
Usage: |
||||
|
from pyngspice import NgspiceRunner |
||||
|
|
||||
|
runner = NgspiceRunner(working_directory="./output") |
||||
|
raw_file, log_file = runner.run("circuit.net") |
||||
|
|
||||
|
# Or with auto-detection: |
||||
|
from pyngspice.runner import get_runner |
||||
|
runner = get_runner("./output") |
||||
|
""" |
||||
|
|
||||
|
import os |
||||
|
import shutil |
||||
|
import subprocess |
||||
|
import sys |
||||
|
from abc import ABC, abstractmethod |
||||
|
from pathlib import Path |
||||
|
from typing import Optional, Tuple |
||||
|
|
||||
|
from .netlist import preprocess_netlist |
||||
|
|
||||
|
|
||||
|
def _build_probe_mapping(processed_netlist_path: str) -> dict: |
||||
|
"""Build a mapping from probe trace names to original capacitor trace names. |
||||
|
|
||||
|
Reads the processed netlist to find V_probe_* lines and builds: |
||||
|
{'i(v_probe_c_mmc)': 'i(c_mmc)', ...} |
||||
|
|
||||
|
All names are lowercased to match ngspice's raw file convention. |
||||
|
|
||||
|
Args: |
||||
|
processed_netlist_path: Path to the preprocessed netlist file |
||||
|
|
||||
|
Returns: |
||||
|
Dict mapping probe trace names to original capacitor trace names |
||||
|
""" |
||||
|
mapping = {} |
||||
|
try: |
||||
|
with open(processed_netlist_path, 'r') as f: |
||||
|
for line in f: |
||||
|
tokens = line.strip().split() |
||||
|
if not tokens: |
||||
|
continue |
||||
|
name = tokens[0] |
||||
|
if name.upper().startswith('V_PROBE_'): |
||||
|
# V_probe_C_mmc -> C_mmc |
||||
|
cap_name = name[8:] # strip 'V_probe_' |
||||
|
old_trace = f'i({name.lower()})' |
||||
|
new_trace = f'i({cap_name.lower()})' |
||||
|
mapping[old_trace] = new_trace |
||||
|
except (OSError, IOError): |
||||
|
pass |
||||
|
return mapping |
||||
|
|
||||
|
|
||||
|
def _postprocess_raw_file(raw_file: str, processed_netlist_path: str) -> None: |
||||
|
"""Rename capacitor probe traces in raw file header. |
||||
|
|
||||
|
After simulation, the raw file contains traces like i(v_probe_c_mmc). |
||||
|
This function renames them back to i(c_mmc) so pyTesla sees the |
||||
|
expected capacitor current names. |
||||
|
|
||||
|
Only modifies the ASCII header; the binary data section is untouched. |
||||
|
|
||||
|
Args: |
||||
|
raw_file: Path to the .raw file to post-process |
||||
|
processed_netlist_path: Path to the preprocessed netlist (to find probes) |
||||
|
""" |
||||
|
mapping = _build_probe_mapping(processed_netlist_path) |
||||
|
if not mapping: |
||||
|
return |
||||
|
|
||||
|
try: |
||||
|
with open(raw_file, 'rb') as f: |
||||
|
content = f.read() |
||||
|
except (OSError, IOError): |
||||
|
return |
||||
|
|
||||
|
# Find header/data boundary |
||||
|
header_end = -1 |
||||
|
for marker in [b'Binary:\n', b'Binary:\r\n', b'Values:\n', b'Values:\r\n']: |
||||
|
pos = content.find(marker) |
||||
|
if pos >= 0: |
||||
|
header_end = pos + len(marker) |
||||
|
break |
||||
|
|
||||
|
if header_end < 0: |
||||
|
return # Can't find boundary, skip |
||||
|
|
||||
|
header = content[:header_end].decode('ascii', errors='replace') |
||||
|
data = content[header_end:] |
||||
|
|
||||
|
# Apply renames in header |
||||
|
for old_name, new_name in mapping.items(): |
||||
|
header = header.replace(old_name, new_name) |
||||
|
|
||||
|
with open(raw_file, 'wb') as f: |
||||
|
f.write(header.encode('ascii')) |
||||
|
f.write(data) |
||||
|
|
||||
|
|
||||
|
class SimulationError(Exception): |
||||
|
"""Raised when a SPICE simulation fails.""" |
||||
|
|
||||
|
def __init__(self, message: str, log_content: str = None): |
||||
|
super().__init__(message) |
||||
|
self.log_content = log_content |
||||
|
|
||||
|
|
||||
|
class SpiceRunner(ABC): |
||||
|
"""Abstract base class for SPICE simulator runners. |
||||
|
|
||||
|
This interface is used by pyTesla to run simulations with different |
||||
|
SPICE backends (LTspice, ngspice embedded, ngspice subprocess). |
||||
|
All implementations produce .raw and .log files that can be parsed |
||||
|
with PyLTSpice's RawRead or pyngspice's RawRead. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, working_directory: str): |
||||
|
"""Initialize with working directory for netlist and output files. |
||||
|
|
||||
|
Args: |
||||
|
working_directory: Directory where output files will be written. |
||||
|
Created if it doesn't exist. |
||||
|
""" |
||||
|
self.working_directory = os.path.abspath(working_directory) |
||||
|
os.makedirs(self.working_directory, exist_ok=True) |
||||
|
|
||||
|
@abstractmethod |
||||
|
def run(self, netlist_path: str, timeout: int = None) -> Tuple[str, str]: |
||||
|
"""Execute SPICE simulation. |
||||
|
|
||||
|
Args: |
||||
|
netlist_path: Absolute or relative path to .net/.cir file |
||||
|
timeout: Optional timeout in seconds |
||||
|
|
||||
|
Returns: |
||||
|
(raw_file_path, log_file_path) - absolute paths to output files |
||||
|
|
||||
|
Raises: |
||||
|
SimulationError: If simulation fails or times out |
||||
|
FileNotFoundError: If netlist file doesn't exist |
||||
|
""" |
||||
|
... |
||||
|
|
||||
|
@staticmethod |
||||
|
@abstractmethod |
||||
|
def detect() -> bool: |
||||
|
"""Return True if this SPICE engine is available.""" |
||||
|
... |
||||
|
|
||||
|
@staticmethod |
||||
|
@abstractmethod |
||||
|
def get_executable_path() -> Optional[str]: |
||||
|
"""Auto-detect and return path to executable, or None.""" |
||||
|
... |
||||
|
|
||||
|
def _preprocess_netlist(self, netlist_path: str) -> str: |
||||
|
"""Pre-process netlist for ngspice compatibility. |
||||
|
|
||||
|
If the netlist needs translation (Rser= on inductors, etc.), |
||||
|
writes a processed copy to the working directory and returns |
||||
|
its path. Otherwise returns the original path. |
||||
|
|
||||
|
Args: |
||||
|
netlist_path: Path to the original netlist |
||||
|
|
||||
|
Returns: |
||||
|
Path to the netlist to actually simulate (may be original or processed copy) |
||||
|
""" |
||||
|
netlist_path = os.path.abspath(netlist_path) |
||||
|
if not os.path.isfile(netlist_path): |
||||
|
raise FileNotFoundError(f"Netlist not found: {netlist_path}") |
||||
|
|
||||
|
with open(netlist_path, 'r') as f: |
||||
|
original = f.read() |
||||
|
|
||||
|
processed = preprocess_netlist(original) |
||||
|
if processed == original: |
||||
|
return netlist_path |
||||
|
|
||||
|
# Write processed netlist to working directory |
||||
|
stem = Path(netlist_path).stem |
||||
|
out_path = os.path.join(self.working_directory, f"{stem}_ngspice.net") |
||||
|
with open(out_path, 'w') as f: |
||||
|
f.write(processed) |
||||
|
return out_path |
||||
|
|
||||
|
|
||||
|
class NgspiceRunner(SpiceRunner): |
||||
|
"""NgspiceRunner using embedded ngspice via pybind11 C++ extension. |
||||
|
|
||||
|
This is the primary runner. It uses the statically-linked ngspice |
||||
|
library through pybind11 bindings — no external ngspice installation |
||||
|
is needed. |
||||
|
|
||||
|
The embedded approach is faster than subprocess invocation since there's |
||||
|
no process spawn overhead and the simulator is already loaded in memory. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, working_directory: str = "."): |
||||
|
super().__init__(working_directory) |
||||
|
from . import _cpp_available |
||||
|
if not _cpp_available: |
||||
|
from . import _cpp_error |
||||
|
raise ImportError( |
||||
|
f"pyngspice C++ extension not available: {_cpp_error}\n" |
||||
|
f"Use SubprocessRunner as a fallback, or rebuild with: pip install -e ." |
||||
|
) |
||||
|
from ._pyngspice import SimRunner as _CppSimRunner |
||||
|
self._runner = _CppSimRunner(output_folder=self.working_directory) |
||||
|
|
||||
|
def run(self, netlist_path: str, timeout: int = None) -> Tuple[str, str]: |
||||
|
"""Run simulation using embedded ngspice. |
||||
|
|
||||
|
Args: |
||||
|
netlist_path: Path to .net/.cir netlist file |
||||
|
timeout: Optional timeout in seconds (not yet implemented for embedded mode) |
||||
|
|
||||
|
Returns: |
||||
|
(raw_file_path, log_file_path) as absolute paths |
||||
|
|
||||
|
Raises: |
||||
|
SimulationError: If simulation fails |
||||
|
""" |
||||
|
processed_path = self._preprocess_netlist(netlist_path) |
||||
|
|
||||
|
try: |
||||
|
raw_file, log_file = self._runner.run_now(processed_path) |
||||
|
except Exception as e: |
||||
|
raise SimulationError(f"Simulation failed: {e}") from e |
||||
|
|
||||
|
# Normalize to absolute paths |
||||
|
raw_file = os.path.abspath(raw_file) |
||||
|
log_file = os.path.abspath(log_file) |
||||
|
|
||||
|
if not os.path.isfile(raw_file): |
||||
|
raise SimulationError( |
||||
|
f"Simulation completed but raw file not found: {raw_file}" |
||||
|
) |
||||
|
|
||||
|
# Post-process: rename capacitor probe traces in raw file header |
||||
|
_postprocess_raw_file(raw_file, processed_path) |
||||
|
|
||||
|
return raw_file, log_file |
||||
|
|
||||
|
@staticmethod |
||||
|
def detect() -> bool: |
||||
|
"""Return True if the embedded C++ extension is available.""" |
||||
|
try: |
||||
|
from . import _cpp_available |
||||
|
return _cpp_available |
||||
|
except ImportError: |
||||
|
return False |
||||
|
|
||||
|
@staticmethod |
||||
|
def get_executable_path() -> Optional[str]: |
||||
|
"""Return None — embedded mode has no separate executable.""" |
||||
|
return None |
||||
|
|
||||
|
|
||||
|
class SubprocessRunner(SpiceRunner): |
||||
|
"""Fallback runner using ngspice as a subprocess. |
||||
|
|
||||
|
Invokes ngspice in batch mode via the command line. Requires ngspice |
||||
|
to be installed and available on PATH (or specified explicitly). |
||||
|
|
||||
|
This is useful when: |
||||
|
- The C++ extension is not compiled |
||||
|
- You need to use a specific ngspice build |
||||
|
- Debugging simulation issues with ngspice's own output |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, working_directory: str = ".", executable: str = None): |
||||
|
"""Initialize with optional explicit path to ngspice executable. |
||||
|
|
||||
|
Args: |
||||
|
working_directory: Directory for output files |
||||
|
executable: Path to ngspice executable. If None, auto-detects. |
||||
|
""" |
||||
|
super().__init__(working_directory) |
||||
|
self._executable = executable or self._find_ngspice() |
||||
|
if self._executable is None: |
||||
|
raise FileNotFoundError( |
||||
|
"ngspice executable not found. Install ngspice or specify " |
||||
|
"the path explicitly via the 'executable' parameter." |
||||
|
) |
||||
|
|
||||
|
def run(self, netlist_path: str, timeout: int = None) -> Tuple[str, str]: |
||||
|
"""Run simulation using ngspice subprocess. |
||||
|
|
||||
|
Invokes: ngspice -b -r <output.raw> -o <output.log> <netlist> |
||||
|
|
||||
|
Args: |
||||
|
netlist_path: Path to .net/.cir netlist file |
||||
|
timeout: Optional timeout in seconds |
||||
|
|
||||
|
Returns: |
||||
|
(raw_file_path, log_file_path) as absolute paths |
||||
|
|
||||
|
Raises: |
||||
|
SimulationError: If simulation fails or times out |
||||
|
""" |
||||
|
processed_path = self._preprocess_netlist(netlist_path) |
||||
|
|
||||
|
stem = Path(processed_path).stem |
||||
|
raw_file = os.path.join(self.working_directory, f"{stem}.raw") |
||||
|
log_file = os.path.join(self.working_directory, f"{stem}.log") |
||||
|
|
||||
|
cmd = [ |
||||
|
self._executable, |
||||
|
'-b', # Batch mode (no GUI) |
||||
|
'-r', raw_file, # Raw output file |
||||
|
'-o', log_file, # Log output file |
||||
|
processed_path, # Input netlist |
||||
|
] |
||||
|
|
||||
|
try: |
||||
|
result = subprocess.run( |
||||
|
cmd, |
||||
|
capture_output=True, |
||||
|
text=True, |
||||
|
timeout=timeout, |
||||
|
cwd=self.working_directory, |
||||
|
) |
||||
|
except subprocess.TimeoutExpired: |
||||
|
raise SimulationError( |
||||
|
f"Simulation timed out after {timeout} seconds" |
||||
|
) |
||||
|
except FileNotFoundError: |
||||
|
raise SimulationError( |
||||
|
f"ngspice executable not found: {self._executable}" |
||||
|
) |
||||
|
|
||||
|
# Check for errors |
||||
|
if result.returncode != 0: |
||||
|
log_content = None |
||||
|
if os.path.isfile(log_file): |
||||
|
with open(log_file, 'r') as f: |
||||
|
log_content = f.read() |
||||
|
raise SimulationError( |
||||
|
f"ngspice exited with code {result.returncode}: {result.stderr}", |
||||
|
log_content=log_content, |
||||
|
) |
||||
|
|
||||
|
if not os.path.isfile(raw_file): |
||||
|
raise SimulationError( |
||||
|
f"Simulation completed but raw file not found: {raw_file}\n" |
||||
|
f"stderr: {result.stderr}" |
||||
|
) |
||||
|
|
||||
|
return os.path.abspath(raw_file), os.path.abspath(log_file) |
||||
|
|
||||
|
@staticmethod |
||||
|
def detect() -> bool: |
||||
|
"""Return True if ngspice executable is found on PATH.""" |
||||
|
return SubprocessRunner._find_ngspice() is not None |
||||
|
|
||||
|
@staticmethod |
||||
|
def get_executable_path() -> Optional[str]: |
||||
|
"""Return path to ngspice executable, or None if not found.""" |
||||
|
return SubprocessRunner._find_ngspice() |
||||
|
|
||||
|
@staticmethod |
||||
|
def _find_ngspice() -> Optional[str]: |
||||
|
"""Search for ngspice executable. |
||||
|
|
||||
|
Checks: |
||||
|
1. PATH (via shutil.which) |
||||
|
2. Common install locations on each platform |
||||
|
""" |
||||
|
# Check PATH first |
||||
|
found = shutil.which("ngspice") |
||||
|
if found: |
||||
|
return found |
||||
|
|
||||
|
# Check common install locations |
||||
|
if sys.platform == 'win32': |
||||
|
candidates = [ |
||||
|
r"C:\Program Files\ngspice\bin\ngspice.exe", |
||||
|
r"C:\Program Files (x86)\ngspice\bin\ngspice.exe", |
||||
|
os.path.expanduser(r"~\ngspice\bin\ngspice.exe"), |
||||
|
] |
||||
|
elif sys.platform == 'darwin': |
||||
|
candidates = [ |
||||
|
"/usr/local/bin/ngspice", |
||||
|
"/opt/homebrew/bin/ngspice", |
||||
|
] |
||||
|
else: # Linux |
||||
|
candidates = [ |
||||
|
"/usr/bin/ngspice", |
||||
|
"/usr/local/bin/ngspice", |
||||
|
"/snap/bin/ngspice", |
||||
|
] |
||||
|
|
||||
|
for path in candidates: |
||||
|
if os.path.isfile(path): |
||||
|
return path |
||||
|
|
||||
|
return None |
||||
|
|
||||
|
|
||||
|
def get_runner(working_directory: str = ".", |
||||
|
backend: str = "auto") -> SpiceRunner: |
||||
|
"""Factory function to get the best available SpiceRunner. |
||||
|
|
||||
|
Args: |
||||
|
working_directory: Directory for simulation output files |
||||
|
backend: One of "auto", "embedded", "subprocess". |
||||
|
- "auto": try embedded first, fall back to subprocess |
||||
|
- "embedded": use NgspiceRunner (requires C++ extension) |
||||
|
- "subprocess": use SubprocessRunner (requires ngspice on PATH) |
||||
|
|
||||
|
Returns: |
||||
|
A SpiceRunner instance |
||||
|
|
||||
|
Raises: |
||||
|
RuntimeError: If no suitable backend is found |
||||
|
""" |
||||
|
if backend == "embedded": |
||||
|
return NgspiceRunner(working_directory) |
||||
|
elif backend == "subprocess": |
||||
|
return SubprocessRunner(working_directory) |
||||
|
elif backend == "auto": |
||||
|
# Try embedded first (faster, no external dependency) |
||||
|
if NgspiceRunner.detect(): |
||||
|
return NgspiceRunner(working_directory) |
||||
|
# Fall back to subprocess |
||||
|
if SubprocessRunner.detect(): |
||||
|
return SubprocessRunner(working_directory) |
||||
|
raise RuntimeError( |
||||
|
"No ngspice backend available. Either:\n" |
||||
|
" 1. Build the C++ extension: pip install -e .\n" |
||||
|
" 2. Install ngspice and add it to PATH" |
||||
|
) |
||||
|
else: |
||||
|
raise ValueError(f"Unknown backend: {backend!r}. Use 'auto', 'embedded', or 'subprocess'.") |
||||
@ -0,0 +1,79 @@ |
|||||
|
[build-system] |
||||
|
requires = [ |
||||
|
"scikit-build-core>=0.8", |
||||
|
"pybind11>=2.11", |
||||
|
"numpy>=1.20", |
||||
|
] |
||||
|
build-backend = "scikit_build_core.build" |
||||
|
|
||||
|
[project] |
||||
|
name = "pyngspice" |
||||
|
version = "43.0.0" |
||||
|
description = "Python bindings for ngspice circuit simulator (pyTesla backend)" |
||||
|
license = {text = "BSD-3-Clause"} |
||||
|
requires-python = ">=3.9" |
||||
|
authors = [ |
||||
|
{name = "ngspice team", email = "ngspice-devel@lists.sourceforge.net"}, |
||||
|
] |
||||
|
keywords = ["spice", "circuit", "simulation", "electronics", "EDA", "tesla-coil"] |
||||
|
classifiers = [ |
||||
|
"Development Status :: 4 - Beta", |
||||
|
"Intended Audience :: Developers", |
||||
|
"Intended Audience :: Science/Research", |
||||
|
"License :: OSI Approved :: BSD License", |
||||
|
"Operating System :: Microsoft :: Windows", |
||||
|
"Operating System :: POSIX :: Linux", |
||||
|
"Operating System :: MacOS", |
||||
|
"Programming Language :: C", |
||||
|
"Programming Language :: C++", |
||||
|
"Programming Language :: Python :: 3", |
||||
|
"Programming Language :: Python :: 3.9", |
||||
|
"Programming Language :: Python :: 3.10", |
||||
|
"Programming Language :: Python :: 3.11", |
||||
|
"Programming Language :: Python :: 3.12", |
||||
|
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", |
||||
|
] |
||||
|
dependencies = [ |
||||
|
"numpy>=1.20", |
||||
|
] |
||||
|
|
||||
|
[project.optional-dependencies] |
||||
|
dev = [ |
||||
|
"pytest>=7.0", |
||||
|
"pytest-cov>=4.0", |
||||
|
] |
||||
|
|
||||
|
[project.urls] |
||||
|
Homepage = "https://ngspice.sourceforge.io/" |
||||
|
Documentation = "https://ngspice.sourceforge.io/docs.html" |
||||
|
Repository = "https://sourceforge.net/p/ngspice/ngspice/" |
||||
|
Issues = "https://sourceforge.net/p/ngspice/bugs/" |
||||
|
|
||||
|
[tool.scikit-build] |
||||
|
cmake.version = ">=3.18" |
||||
|
cmake.build-type = "Release" |
||||
|
cmake.args = ["-G", "MinGW Makefiles", "-DCMAKE_VERBOSE_MAKEFILE=ON"] |
||||
|
ninja.make-fallback = true |
||||
|
wheel.packages = ["pyngspice"] |
||||
|
wheel.install-dir = "pyngspice" |
||||
|
build-dir = "build/{wheel_tag}" |
||||
|
editable.mode = "inplace" |
||||
|
|
||||
|
[tool.scikit-build.cmake.define] |
||||
|
BUILD_PYTHON_BINDINGS = "ON" |
||||
|
ENABLE_XSPICE = "ON" |
||||
|
ENABLE_OSDI = "ON" |
||||
|
|
||||
|
[tool.cibuildwheel] |
||||
|
build = "cp39-* cp310-* cp311-* cp312-*" |
||||
|
skip = "*-musllinux_* *-manylinux_i686" |
||||
|
|
||||
|
[tool.cibuildwheel.linux] |
||||
|
before-all = "yum install -y bison flex" |
||||
|
manylinux-x86_64-image = "manylinux2014" |
||||
|
|
||||
|
[tool.cibuildwheel.macos] |
||||
|
before-all = "brew install bison flex" |
||||
|
|
||||
|
[tool.cibuildwheel.windows] |
||||
|
before-all = "choco install winflexbison3 -y" |
||||
@ -0,0 +1,340 @@ |
|||||
|
/**
|
||||
|
* @file module.cpp |
||||
|
* @brief Main pybind11 module definition for pyngspice Python extension |
||||
|
* |
||||
|
* This file defines the Python module interface using pybind11, |
||||
|
* exposing the ngspice simulation classes to Python. |
||||
|
*/ |
||||
|
|
||||
|
#include <pybind11/pybind11.h>
|
||||
|
#include <pybind11/stl.h>
|
||||
|
#include <pybind11/numpy.h>
|
||||
|
#include <pybind11/functional.h>
|
||||
|
|
||||
|
#include "simulator.h"
|
||||
|
#include "sim_runner.h"
|
||||
|
#include "raw_read.h"
|
||||
|
#include "trace.h"
|
||||
|
|
||||
|
namespace py = pybind11; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Convert std::vector<double> to numpy array |
||||
|
*/ |
||||
|
py::array_t<double> vector_to_numpy(const std::vector<double>& vec) { |
||||
|
return py::array_t<double>(vec.size(), vec.data()); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Convert std::vector<std::complex<double>> to numpy array |
||||
|
*/ |
||||
|
py::array_t<std::complex<double>> complex_vector_to_numpy( |
||||
|
const std::vector<std::complex<double>>& vec) { |
||||
|
return py::array_t<std::complex<double>>(vec.size(), vec.data()); |
||||
|
} |
||||
|
|
||||
|
PYBIND11_MODULE(_pyngspice, m) { |
||||
|
m.doc() = R"pbdoc( |
||||
|
pyngspice Python Extension |
||||
|
-------------------------- |
||||
|
|
||||
|
Native Python bindings for the ngspice circuit simulator. |
||||
|
Provides a PyLTSpice-compatible API for running simulations |
||||
|
and accessing results. Designed as a backend for pyTesla. |
||||
|
|
||||
|
Basic usage: |
||||
|
from pyngspice import SimRunner, RawRead |
||||
|
|
||||
|
runner = SimRunner(output_folder="./output") |
||||
|
raw_file, log_file = runner.run_now("circuit.net") |
||||
|
|
||||
|
raw = RawRead(raw_file) |
||||
|
trace = raw.get_trace("V(out)") |
||||
|
data = trace.get_wave(0) # numpy array |
||||
|
)pbdoc"; |
||||
|
|
||||
|
m.attr("__version__") = "43.0.0"; |
||||
|
|
||||
|
// =========================================================================
|
||||
|
// Trace class
|
||||
|
// =========================================================================
|
||||
|
py::class_<ngspice::Trace, std::shared_ptr<ngspice::Trace>>(m, "Trace", |
||||
|
R"pbdoc( |
||||
|
Represents a single trace/waveform from simulation results. |
||||
|
|
||||
|
Provides access to simulation data as numpy arrays. |
||||
|
Supports both real and complex data (for AC analysis). |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def_property_readonly("name", &ngspice::Trace::name, |
||||
|
"The trace name (e.g., 'V(out)')") |
||||
|
|
||||
|
.def_property_readonly("is_complex", &ngspice::Trace::is_complex, |
||||
|
"True if trace contains complex data") |
||||
|
|
||||
|
.def("get_wave", [](const ngspice::Trace& self, size_t step) { |
||||
|
return vector_to_numpy(self.get_wave(step)); |
||||
|
}, |
||||
|
py::arg("step") = 0, |
||||
|
R"pbdoc( |
||||
|
Get waveform data for a specific step. |
||||
|
|
||||
|
Args: |
||||
|
step: Step index (0 for single-step simulations) |
||||
|
|
||||
|
Returns: |
||||
|
numpy array of data points |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def("get_wave_complex", [](const ngspice::Trace& self, size_t step) { |
||||
|
return complex_vector_to_numpy(self.get_wave_complex(step)); |
||||
|
}, |
||||
|
py::arg("step") = 0, |
||||
|
"Get complex waveform data for a specific step") |
||||
|
|
||||
|
.def("__len__", &ngspice::Trace::size) |
||||
|
|
||||
|
.def("__repr__", [](const ngspice::Trace& self) { |
||||
|
return "<Trace '" + self.name() + "' (" + |
||||
|
std::to_string(self.size()) + " points)>"; |
||||
|
}); |
||||
|
|
||||
|
// =========================================================================
|
||||
|
// RawRead class
|
||||
|
// =========================================================================
|
||||
|
py::class_<ngspice::RawRead>(m, "RawRead", |
||||
|
R"pbdoc( |
||||
|
Parser for ngspice .raw simulation output files. |
||||
|
|
||||
|
Provides PyLTSpice-compatible API for accessing trace data. |
||||
|
Supports both ASCII and binary raw file formats. |
||||
|
|
||||
|
Example: |
||||
|
raw = RawRead("simulation.raw") |
||||
|
traces = raw.get_trace_names() |
||||
|
voltage = raw.get_trace("V(out)").get_wave(0) |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def(py::init<const std::string&>(), |
||||
|
py::arg("filepath"), |
||||
|
"Load and parse a .raw file") |
||||
|
|
||||
|
.def(py::init<>(), |
||||
|
"Create an empty RawRead object") |
||||
|
|
||||
|
.def_property_readonly("title", &ngspice::RawRead::title, |
||||
|
"The plot title from the raw file") |
||||
|
|
||||
|
.def_property_readonly("date", &ngspice::RawRead::date, |
||||
|
"The simulation date") |
||||
|
|
||||
|
.def_property_readonly("plot_name", &ngspice::RawRead::plot_name, |
||||
|
"The plot name") |
||||
|
|
||||
|
.def_property_readonly("analysis_type", &ngspice::RawRead::analysis_type, |
||||
|
"The analysis type (tran, ac, dc, etc.)") |
||||
|
|
||||
|
.def("get_trace_names", &ngspice::RawRead::get_trace_names, |
||||
|
"Get list of all trace names") |
||||
|
|
||||
|
.def("get_trace", &ngspice::RawRead::get_trace, |
||||
|
py::arg("name"), |
||||
|
R"pbdoc( |
||||
|
Get a specific trace by name. |
||||
|
|
||||
|
Args: |
||||
|
name: Trace name (e.g., "V(out)" or "time") |
||||
|
|
||||
|
Returns: |
||||
|
Trace object |
||||
|
|
||||
|
Raises: |
||||
|
KeyError: If trace not found |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def("has_trace", &ngspice::RawRead::has_trace, |
||||
|
py::arg("name"), |
||||
|
"Check if a trace exists") |
||||
|
|
||||
|
.def("get_steps", &ngspice::RawRead::get_steps, |
||||
|
"Get list of step indices (for parametric sweeps)") |
||||
|
|
||||
|
.def_property_readonly("steps", &ngspice::RawRead::steps, |
||||
|
"Step parameter values (for .step simulations)") |
||||
|
|
||||
|
.def_property_readonly("num_points", &ngspice::RawRead::num_points, |
||||
|
"Number of data points") |
||||
|
|
||||
|
.def_property_readonly("num_variables", &ngspice::RawRead::num_variables, |
||||
|
"Number of variables/traces") |
||||
|
|
||||
|
.def_property_readonly("is_complex", &ngspice::RawRead::is_complex, |
||||
|
"True if data is complex (AC analysis)") |
||||
|
|
||||
|
.def_property_readonly("is_real", &ngspice::RawRead::is_real, |
||||
|
"True if data is real") |
||||
|
|
||||
|
.def("__repr__", [](const ngspice::RawRead& self) { |
||||
|
return "<RawRead '" + self.filepath() + "' (" + |
||||
|
std::to_string(self.num_variables()) + " traces, " + |
||||
|
std::to_string(self.num_points()) + " points)>"; |
||||
|
}) |
||||
|
|
||||
|
.def("__contains__", &ngspice::RawRead::has_trace) |
||||
|
|
||||
|
.def("__getitem__", &ngspice::RawRead::get_trace); |
||||
|
|
||||
|
// =========================================================================
|
||||
|
// SimRunner class
|
||||
|
// =========================================================================
|
||||
|
py::class_<ngspice::SimRunner>(m, "SimRunner", |
||||
|
R"pbdoc( |
||||
|
PyLTSpice-compatible simulation runner. |
||||
|
|
||||
|
Provides methods for running ngspice simulations on netlists |
||||
|
and retrieving results. |
||||
|
|
||||
|
Example: |
||||
|
runner = SimRunner(output_folder="./output") |
||||
|
raw_file, log_file = runner.run_now("circuit.net") |
||||
|
|
||||
|
# Or async:
|
||||
|
runner.run("circuit.net") |
||||
|
for raw_file, log_file in runner: |
||||
|
process_results(raw_file) |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def(py::init([](const std::string& output_folder, py::object /*simulator*/) { |
||||
|
// simulator argument is ignored - for PyLTSpice API compatibility
|
||||
|
return std::make_unique<ngspice::SimRunner>(output_folder, nullptr); |
||||
|
}), |
||||
|
py::arg("output_folder") = ".", |
||||
|
py::arg("simulator") = py::none(), |
||||
|
R"pbdoc( |
||||
|
Create a SimRunner. |
||||
|
|
||||
|
Args: |
||||
|
output_folder: Directory for output files |
||||
|
simulator: Simulator class (ignored, for API compatibility) |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def("run_now", &ngspice::SimRunner::run_now, |
||||
|
py::arg("netlist"), |
||||
|
R"pbdoc( |
||||
|
Run simulation synchronously (blocking). |
||||
|
|
||||
|
Args: |
||||
|
netlist: Path to the netlist file |
||||
|
|
||||
|
Returns: |
||||
|
Tuple of (raw_file_path, log_file_path) |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def("run", &ngspice::SimRunner::run, |
||||
|
py::arg("netlist"), |
||||
|
R"pbdoc( |
||||
|
Queue a simulation for async execution. |
||||
|
|
||||
|
Args: |
||||
|
netlist: Path to the netlist file |
||||
|
|
||||
|
Returns: |
||||
|
True if simulation was queued |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def("wait_completion", &ngspice::SimRunner::wait_completion, |
||||
|
"Wait for all queued simulations to complete") |
||||
|
|
||||
|
.def_property_readonly("output_folder", &ngspice::SimRunner::output_folder, |
||||
|
"Output folder path") |
||||
|
|
||||
|
.def_property("parallel_sims", |
||||
|
&ngspice::SimRunner::parallel_sims, |
||||
|
&ngspice::SimRunner::set_parallel_sims, |
||||
|
"Maximum parallel simulations") |
||||
|
|
||||
|
.def("__iter__", [](ngspice::SimRunner& self) { |
||||
|
return py::make_iterator(self.begin(), self.end()); |
||||
|
}, py::keep_alive<0, 1>()); |
||||
|
|
||||
|
// =========================================================================
|
||||
|
// NgSpice class (compatibility)
|
||||
|
// =========================================================================
|
||||
|
py::class_<ngspice::NgSpice>(m, "NgSpice", |
||||
|
R"pbdoc( |
||||
|
NgSpice simulator class (for PyLTSpice compatibility). |
||||
|
|
||||
|
This class exists for API compatibility with PyLTSpice. |
||||
|
Since we use the embedded ngspice library, most settings |
||||
|
are ignored. |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def_readwrite_static("spice_exe", &ngspice::NgSpice::spice_exe, |
||||
|
"Path to ngspice executable (ignored - using embedded ngspice)") |
||||
|
|
||||
|
.def_static("version", &ngspice::NgSpice::version, |
||||
|
"Get the ngspice version string"); |
||||
|
|
||||
|
// =========================================================================
|
||||
|
// Simulator class (advanced usage)
|
||||
|
// =========================================================================
|
||||
|
py::class_<ngspice::Simulator>(m, "Simulator", |
||||
|
R"pbdoc( |
||||
|
Low-level ngspice simulator wrapper. |
||||
|
|
||||
|
For advanced usage when you need direct control over |
||||
|
the simulation engine. Most users should use SimRunner. |
||||
|
)pbdoc") |
||||
|
|
||||
|
.def(py::init<int>(), |
||||
|
py::arg("instance_id") = 0, |
||||
|
"Create a Simulator instance") |
||||
|
|
||||
|
.def("initialize", &ngspice::Simulator::initialize, |
||||
|
"Initialize the ngspice engine") |
||||
|
|
||||
|
.def("is_initialized", &ngspice::Simulator::is_initialized, |
||||
|
"Check if simulator is initialized") |
||||
|
|
||||
|
.def("load_netlist", &ngspice::Simulator::load_netlist, |
||||
|
py::arg("filepath"), |
||||
|
"Load a netlist from file") |
||||
|
|
||||
|
.def("command", &ngspice::Simulator::command, |
||||
|
py::arg("command"), |
||||
|
"Execute a SPICE command") |
||||
|
|
||||
|
.def("run", &ngspice::Simulator::run, |
||||
|
"Run the simulation (blocking)") |
||||
|
|
||||
|
.def("run_async", &ngspice::Simulator::run_async, |
||||
|
"Run simulation in background thread") |
||||
|
|
||||
|
.def("is_running", &ngspice::Simulator::is_running, |
||||
|
"Check if simulation is currently running") |
||||
|
|
||||
|
.def("halt", &ngspice::Simulator::halt, |
||||
|
"Halt a running simulation") |
||||
|
|
||||
|
.def("resume", &ngspice::Simulator::resume, |
||||
|
"Resume a halted simulation") |
||||
|
|
||||
|
.def("reset", &ngspice::Simulator::reset, |
||||
|
"Reset the simulator state") |
||||
|
|
||||
|
.def("current_plot", &ngspice::Simulator::current_plot, |
||||
|
"Get the current plot name") |
||||
|
|
||||
|
.def("all_plots", &ngspice::Simulator::all_plots, |
||||
|
"Get all plot names") |
||||
|
|
||||
|
.def("all_vectors", &ngspice::Simulator::all_vectors, |
||||
|
py::arg("plot_name") = "", |
||||
|
"Get all vector names in a plot") |
||||
|
|
||||
|
.def("get_output", &ngspice::Simulator::get_output, |
||||
|
"Get accumulated output messages") |
||||
|
|
||||
|
.def("clear_output", &ngspice::Simulator::clear_output, |
||||
|
"Clear accumulated output"); |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
/**
|
||||
|
* @file callbacks.cpp |
||||
|
* @brief Callback handler implementations |
||||
|
*/ |
||||
|
|
||||
|
#include "callbacks.h"
|
||||
|
#include <regex>
|
||||
|
#include <map>
|
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
CallbackManager& CallbackManager::instance() { |
||||
|
static CallbackManager manager; |
||||
|
return manager; |
||||
|
} |
||||
|
|
||||
|
void CallbackManager::register_callbacks(int instance_id, |
||||
|
OutputCallback output_cb, |
||||
|
StatusCallback status_cb, |
||||
|
ExitCallback exit_cb, |
||||
|
ThreadCallback thread_cb) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
InstanceCallbacks& cbs = callbacks_[instance_id]; |
||||
|
cbs.output = std::move(output_cb); |
||||
|
cbs.status = std::move(status_cb); |
||||
|
cbs.exit = std::move(exit_cb); |
||||
|
cbs.thread = std::move(thread_cb); |
||||
|
} |
||||
|
|
||||
|
void CallbackManager::unregister_callbacks(int instance_id) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
callbacks_.erase(instance_id); |
||||
|
} |
||||
|
|
||||
|
void CallbackManager::on_output(int instance_id, const std::string& message) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
auto it = callbacks_.find(instance_id); |
||||
|
if (it != callbacks_.end() && it->second.output) { |
||||
|
it->second.output(message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void CallbackManager::on_status(int instance_id, const std::string& status) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
auto it = callbacks_.find(instance_id); |
||||
|
if (it != callbacks_.end() && it->second.status) { |
||||
|
it->second.status(status); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void CallbackManager::on_exit(int instance_id, int status, bool immediate, bool quit) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
auto it = callbacks_.find(instance_id); |
||||
|
if (it != callbacks_.end() && it->second.exit) { |
||||
|
it->second.exit(status, immediate, quit); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void CallbackManager::on_thread_status(int instance_id, bool running) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
auto it = callbacks_.find(instance_id); |
||||
|
if (it != callbacks_.end() && it->second.thread) { |
||||
|
it->second.thread(running); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
double parse_progress(const std::string& status) { |
||||
|
// Try to parse progress from various ngspice status formats
|
||||
|
// Examples:
|
||||
|
// "Reference value : 1.00000e-01"
|
||||
|
// "Transient Analysis: 50% complete"
|
||||
|
// "--dc: 1 of 10"
|
||||
|
|
||||
|
// Try percentage format
|
||||
|
std::regex percent_re(R"((\d+(?:\.\d+)?)\s*%\s*complete)", std::regex::icase); |
||||
|
std::smatch match; |
||||
|
if (std::regex_search(status, match, percent_re)) { |
||||
|
return std::stod(match[1].str()) / 100.0; |
||||
|
} |
||||
|
|
||||
|
// Try "X of Y" format
|
||||
|
std::regex of_re(R"((\d+)\s+of\s+(\d+))"); |
||||
|
if (std::regex_search(status, match, of_re)) { |
||||
|
double current = std::stod(match[1].str()); |
||||
|
double total = std::stod(match[2].str()); |
||||
|
if (total > 0) { |
||||
|
return current / total; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Try reference value (for transient analysis)
|
||||
|
std::regex ref_re(R"(Reference value\s*:\s*([+-]?\d+\.?\d*(?:[eE][+-]?\d+)?))"); |
||||
|
if (std::regex_search(status, match, ref_re)) { |
||||
|
// Can't determine progress without knowing the end time
|
||||
|
return -1.0; |
||||
|
} |
||||
|
|
||||
|
return -1.0; // Unknown progress
|
||||
|
} |
||||
|
|
||||
|
} // namespace ngspice
|
||||
@ -0,0 +1,100 @@ |
|||||
|
/** |
||||
|
* @file callbacks.h |
||||
|
* @brief Callback handler declarations for ngspice Python extension |
||||
|
* |
||||
|
* This file declares the callback functions that are registered with |
||||
|
* ngspice to receive simulation events, output, and data. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef NGSPICE_PYTHON_CALLBACKS_H |
||||
|
#define NGSPICE_PYTHON_CALLBACKS_H |
||||
|
|
||||
|
#include <string> |
||||
|
#include <functional> |
||||
|
#include <mutex> |
||||
|
#include <map> |
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
/** |
||||
|
* @brief Callback manager for routing ngspice callbacks |
||||
|
* |
||||
|
* Since ngspice uses C-style callbacks with a user data pointer, |
||||
|
* we need a mechanism to route callbacks to the correct Python objects. |
||||
|
*/ |
||||
|
class CallbackManager { |
||||
|
public: |
||||
|
// Callback types |
||||
|
using OutputCallback = std::function<void(const std::string& message)>; |
||||
|
using StatusCallback = std::function<void(const std::string& status)>; |
||||
|
using ExitCallback = std::function<void(int status, bool immediate, bool quit)>; |
||||
|
using ProgressCallback = std::function<void(double progress)>; |
||||
|
using ThreadCallback = std::function<void(bool running)>; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the singleton instance |
||||
|
*/ |
||||
|
static CallbackManager& instance(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Register callbacks for an instance |
||||
|
*/ |
||||
|
void register_callbacks(int instance_id, |
||||
|
OutputCallback output_cb, |
||||
|
StatusCallback status_cb, |
||||
|
ExitCallback exit_cb, |
||||
|
ThreadCallback thread_cb); |
||||
|
|
||||
|
/** |
||||
|
* @brief Unregister callbacks for an instance |
||||
|
*/ |
||||
|
void unregister_callbacks(int instance_id); |
||||
|
|
||||
|
/** |
||||
|
* @brief Route output message to registered callback |
||||
|
*/ |
||||
|
void on_output(int instance_id, const std::string& message); |
||||
|
|
||||
|
/** |
||||
|
* @brief Route status message to registered callback |
||||
|
*/ |
||||
|
void on_status(int instance_id, const std::string& status); |
||||
|
|
||||
|
/** |
||||
|
* @brief Route exit event to registered callback |
||||
|
*/ |
||||
|
void on_exit(int instance_id, int status, bool immediate, bool quit); |
||||
|
|
||||
|
/** |
||||
|
* @brief Route thread status to registered callback |
||||
|
*/ |
||||
|
void on_thread_status(int instance_id, bool running); |
||||
|
|
||||
|
private: |
||||
|
CallbackManager() = default; |
||||
|
~CallbackManager() = default; |
||||
|
|
||||
|
CallbackManager(const CallbackManager&) = delete; |
||||
|
CallbackManager& operator=(const CallbackManager&) = delete; |
||||
|
|
||||
|
struct InstanceCallbacks { |
||||
|
OutputCallback output; |
||||
|
StatusCallback status; |
||||
|
ExitCallback exit; |
||||
|
ThreadCallback thread; |
||||
|
}; |
||||
|
|
||||
|
std::mutex mutex_; |
||||
|
std::map<int, InstanceCallbacks> callbacks_; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @brief Parse ngspice status string for progress |
||||
|
* @param status Status string like "Reference value : 1.00000e-01" |
||||
|
* @return Progress value 0.0-1.0, or -1.0 if not parseable |
||||
|
*/ |
||||
|
double parse_progress(const std::string& status); |
||||
|
|
||||
|
} // namespace ngspice |
||||
|
|
||||
|
#endif // NGSPICE_PYTHON_CALLBACKS_H |
||||
@ -0,0 +1,341 @@ |
|||||
|
/**
|
||||
|
* @file raw_read.cpp |
||||
|
* @brief RawRead class implementation - parses ngspice .raw files |
||||
|
*/ |
||||
|
|
||||
|
#include "raw_read.h"
|
||||
|
#include <fstream>
|
||||
|
#include <sstream>
|
||||
|
#include <algorithm>
|
||||
|
#include <cstring>
|
||||
|
#include <cctype>
|
||||
|
#include <stdexcept>
|
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
// Trim whitespace from string
|
||||
|
std::string trim(const std::string& str) { |
||||
|
size_t start = str.find_first_not_of(" \t\r\n"); |
||||
|
if (start == std::string::npos) return ""; |
||||
|
size_t end = str.find_last_not_of(" \t\r\n"); |
||||
|
return str.substr(start, end - start + 1); |
||||
|
} |
||||
|
|
||||
|
// Case-insensitive string compare
|
||||
|
bool iequals(const std::string& a, const std::string& b) { |
||||
|
if (a.size() != b.size()) return false; |
||||
|
for (size_t i = 0; i < a.size(); ++i) { |
||||
|
if (std::tolower(a[i]) != std::tolower(b[i])) return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// Parse a header line "Key: Value"
|
||||
|
std::pair<std::string, std::string> parse_header_line(const std::string& line) { |
||||
|
size_t colon = line.find(':'); |
||||
|
if (colon == std::string::npos) { |
||||
|
return {"", ""}; |
||||
|
} |
||||
|
return {trim(line.substr(0, colon)), trim(line.substr(colon + 1))}; |
||||
|
} |
||||
|
|
||||
|
} // anonymous namespace
|
||||
|
|
||||
|
RawRead::RawRead(const std::string& filepath) { |
||||
|
if (!parse(filepath)) { |
||||
|
throw std::runtime_error("Failed to parse raw file: " + filepath); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool RawRead::parse(const std::string& filepath) { |
||||
|
filepath_ = filepath; |
||||
|
|
||||
|
std::ifstream file(filepath, std::ios::binary); |
||||
|
if (!file.is_open()) { |
||||
|
throw std::runtime_error("Cannot open file: " + filepath); |
||||
|
} |
||||
|
|
||||
|
// Parse header
|
||||
|
if (!parse_header(file)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Parse data section
|
||||
|
if (is_binary_) { |
||||
|
if (!parse_binary_data(file)) { |
||||
|
return false; |
||||
|
} |
||||
|
} else { |
||||
|
if (!parse_ascii_data(file)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Detect step boundaries
|
||||
|
detect_steps(); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool RawRead::parse_header(std::istream& file) { |
||||
|
std::string line; |
||||
|
bool in_variables = false; |
||||
|
int var_index = 0; |
||||
|
|
||||
|
while (std::getline(file, line)) { |
||||
|
line = trim(line); |
||||
|
|
||||
|
if (line.empty()) continue; |
||||
|
|
||||
|
// Check for end of header markers
|
||||
|
if (iequals(line, "Binary:")) { |
||||
|
is_binary_ = true; |
||||
|
break; |
||||
|
} |
||||
|
if (iequals(line, "Values:")) { |
||||
|
is_binary_ = false; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// Check for variables section
|
||||
|
if (line.find("Variables:") == 0) { |
||||
|
in_variables = true; |
||||
|
var_index = 0; |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (in_variables) { |
||||
|
// Parse variable definition: "index name type"
|
||||
|
std::istringstream iss(line); |
||||
|
int idx; |
||||
|
std::string name, type; |
||||
|
|
||||
|
if (iss >> idx >> name >> type) { |
||||
|
VarInfo info; |
||||
|
info.index = idx; |
||||
|
info.name = name; |
||||
|
info.type = type; |
||||
|
var_info_.push_back(info); |
||||
|
|
||||
|
// Determine if complex based on type
|
||||
|
if (type == "voltage" || type == "current") { |
||||
|
// Will be set based on flags
|
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// Parse header key-value pairs
|
||||
|
auto [key, value] = parse_header_line(line); |
||||
|
if (key.empty()) continue; |
||||
|
|
||||
|
if (iequals(key, "Title")) { |
||||
|
title_ = value; |
||||
|
} else if (iequals(key, "Date")) { |
||||
|
date_ = value; |
||||
|
} else if (iequals(key, "Plotname")) { |
||||
|
plot_name_ = value; |
||||
|
// Extract analysis type from plot name
|
||||
|
if (plot_name_.find("Transient") != std::string::npos) { |
||||
|
analysis_type_ = "tran"; |
||||
|
} else if (plot_name_.find("AC") != std::string::npos) { |
||||
|
analysis_type_ = "ac"; |
||||
|
is_complex_ = true; // AC analysis has complex data
|
||||
|
} else if (plot_name_.find("DC") != std::string::npos) { |
||||
|
analysis_type_ = "dc"; |
||||
|
} else if (plot_name_.find("Operating Point") != std::string::npos) { |
||||
|
analysis_type_ = "op"; |
||||
|
} |
||||
|
} else if (iequals(key, "Flags")) { |
||||
|
flags_ = value; |
||||
|
if (value.find("complex") != std::string::npos) { |
||||
|
is_complex_ = true; |
||||
|
} |
||||
|
} else if (iequals(key, "No. Variables")) { |
||||
|
num_variables_ = std::stoul(value); |
||||
|
} else if (iequals(key, "No. Points")) { |
||||
|
num_points_ = std::stoul(value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Create trace objects for each variable
|
||||
|
for (const auto& var : var_info_) { |
||||
|
auto trace = std::make_shared<Trace>(var.name, is_complex_); |
||||
|
trace->reserve(num_points_); |
||||
|
traces_[var.name] = trace; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool RawRead::parse_ascii_data(std::istream& file) { |
||||
|
std::string line; |
||||
|
size_t point_index = 0; |
||||
|
|
||||
|
while (std::getline(file, line) && point_index < num_points_) { |
||||
|
line = trim(line); |
||||
|
if (line.empty()) continue; |
||||
|
|
||||
|
// Check for step marker (ngspice uses point index 0 to mark steps)
|
||||
|
size_t tab_pos = line.find('\t'); |
||||
|
if (tab_pos != std::string::npos) { |
||||
|
// Format: "index\tvalue" or "index\treal,imag"
|
||||
|
std::string index_str = line.substr(0, tab_pos); |
||||
|
std::string value_str = trim(line.substr(tab_pos + 1)); |
||||
|
|
||||
|
int idx = std::stoi(index_str); |
||||
|
|
||||
|
// Mark step boundary if index is 0 and not first point
|
||||
|
if (idx == 0 && point_index > 0) { |
||||
|
for (auto& [name, trace] : traces_) { |
||||
|
trace->mark_step_boundary(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Parse value for first variable (usually time/frequency)
|
||||
|
if (is_complex_) { |
||||
|
// Complex format: "real,imag"
|
||||
|
size_t comma = value_str.find(','); |
||||
|
if (comma != std::string::npos) { |
||||
|
double real = std::stod(value_str.substr(0, comma)); |
||||
|
double imag = std::stod(value_str.substr(comma + 1)); |
||||
|
traces_[var_info_[0].name]->add_point(real, imag); |
||||
|
} |
||||
|
} else { |
||||
|
double value = std::stod(value_str); |
||||
|
traces_[var_info_[0].name]->add_point(value); |
||||
|
} |
||||
|
|
||||
|
// Read remaining variables
|
||||
|
for (size_t i = 1; i < var_info_.size(); ++i) { |
||||
|
if (!std::getline(file, line)) break; |
||||
|
line = trim(line); |
||||
|
|
||||
|
if (is_complex_) { |
||||
|
size_t comma = line.find(','); |
||||
|
if (comma != std::string::npos) { |
||||
|
double real = std::stod(line.substr(0, comma)); |
||||
|
double imag = std::stod(line.substr(comma + 1)); |
||||
|
traces_[var_info_[i].name]->add_point(real, imag); |
||||
|
} |
||||
|
} else { |
||||
|
double value = std::stod(line); |
||||
|
traces_[var_info_[i].name]->add_point(value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
point_index++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool RawRead::parse_binary_data(std::istream& file) { |
||||
|
// ngspice binary format:
|
||||
|
// - Real data: double values, interleaved by variable
|
||||
|
// - Complex data: pairs of doubles (real, imag), interleaved
|
||||
|
|
||||
|
size_t value_size = sizeof(double); |
||||
|
size_t point_size = is_complex_ |
||||
|
? num_variables_ * 2 * value_size // real + imag for each var
|
||||
|
: num_variables_ * value_size; // just real for each var
|
||||
|
|
||||
|
std::vector<char> buffer(point_size); |
||||
|
|
||||
|
for (size_t point = 0; point < num_points_; ++point) { |
||||
|
if (!file.read(buffer.data(), point_size)) { |
||||
|
// Partial read is OK - might have fewer points than declared
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// Check for step boundary (scale variable resets to initial value)
|
||||
|
// This is a heuristic - ngspice may restart the scale var
|
||||
|
|
||||
|
if (is_complex_) { |
||||
|
// Complex data: read pairs of doubles
|
||||
|
const double* data = reinterpret_cast<const double*>(buffer.data()); |
||||
|
for (size_t i = 0; i < num_variables_; ++i) { |
||||
|
double real = data[i * 2]; |
||||
|
double imag = data[i * 2 + 1]; |
||||
|
traces_[var_info_[i].name]->add_point(real, imag); |
||||
|
} |
||||
|
} else { |
||||
|
// Real data: read doubles
|
||||
|
const double* data = reinterpret_cast<const double*>(buffer.data()); |
||||
|
for (size_t i = 0; i < num_variables_; ++i) { |
||||
|
traces_[var_info_[i].name]->add_point(data[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void RawRead::detect_steps() { |
||||
|
// For stepped simulations (.step), detect where the scale variable
|
||||
|
// (usually time or frequency) resets to a lower value
|
||||
|
|
||||
|
if (traces_.empty() || var_info_.empty()) return; |
||||
|
|
||||
|
auto scale_trace = traces_[var_info_[0].name]; |
||||
|
const auto& data = scale_trace->get_all_real_data(); |
||||
|
|
||||
|
if (data.size() < 2) return; |
||||
|
|
||||
|
std::vector<size_t> boundaries; |
||||
|
boundaries.push_back(0); // First step starts at 0
|
||||
|
|
||||
|
// Look for decreases in the scale variable (indicates new step)
|
||||
|
for (size_t i = 1; i < data.size(); ++i) { |
||||
|
if (data[i] < data[i - 1] * 0.9) { // 10% tolerance
|
||||
|
boundaries.push_back(i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (boundaries.size() > 1) { |
||||
|
// Update all traces with step boundaries
|
||||
|
for (auto& [name, trace] : traces_) { |
||||
|
trace->set_step_offsets(boundaries); |
||||
|
} |
||||
|
|
||||
|
// Create step_params_ (we don't have parameter names from raw file)
|
||||
|
for (size_t i = 0; i < boundaries.size(); ++i) { |
||||
|
step_params_.push_back({{"step", static_cast<double>(i)}}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
std::vector<std::string> RawRead::get_trace_names() const { |
||||
|
std::vector<std::string> names; |
||||
|
names.reserve(var_info_.size()); |
||||
|
for (const auto& var : var_info_) { |
||||
|
names.push_back(var.name); |
||||
|
} |
||||
|
return names; |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<Trace> RawRead::get_trace(const std::string& name) const { |
||||
|
auto it = traces_.find(name); |
||||
|
if (it == traces_.end()) { |
||||
|
throw std::out_of_range("Trace not found: " + name); |
||||
|
} |
||||
|
return it->second; |
||||
|
} |
||||
|
|
||||
|
bool RawRead::has_trace(const std::string& name) const { |
||||
|
return traces_.find(name) != traces_.end(); |
||||
|
} |
||||
|
|
||||
|
std::vector<int> RawRead::get_steps() const { |
||||
|
std::vector<int> steps; |
||||
|
size_t num = step_params_.empty() ? 1 : step_params_.size(); |
||||
|
for (size_t i = 0; i < num; ++i) { |
||||
|
steps.push_back(static_cast<int>(i)); |
||||
|
} |
||||
|
return steps; |
||||
|
} |
||||
|
|
||||
|
} // namespace ngspice
|
||||
@ -0,0 +1,183 @@ |
|||||
|
/** |
||||
|
* @file raw_read.h |
||||
|
* @brief RawRead class for parsing ngspice .raw files |
||||
|
* |
||||
|
* This class provides PyLTSpice-compatible API for reading |
||||
|
* ngspice simulation result files. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef NGSPICE_PYTHON_RAW_READ_H |
||||
|
#define NGSPICE_PYTHON_RAW_READ_H |
||||
|
|
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
#include <map> |
||||
|
#include <memory> |
||||
|
#include "trace.h" |
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
/** |
||||
|
* @brief Parser for ngspice .raw simulation output files |
||||
|
* |
||||
|
* Supports both ASCII and binary raw file formats. |
||||
|
* Provides PyLTSpice-compatible API for accessing trace data. |
||||
|
*/ |
||||
|
class RawRead { |
||||
|
public: |
||||
|
/** |
||||
|
* @brief Construct a RawRead and parse a file |
||||
|
* @param filepath Path to the .raw file |
||||
|
* @throws std::runtime_error if file cannot be parsed |
||||
|
*/ |
||||
|
explicit RawRead(const std::string& filepath); |
||||
|
|
||||
|
/** |
||||
|
* @brief Default constructor (creates empty object) |
||||
|
*/ |
||||
|
RawRead() = default; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the plot title |
||||
|
*/ |
||||
|
const std::string& title() const { return title_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the plot date |
||||
|
*/ |
||||
|
const std::string& date() const { return date_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the plot name (analysis type) |
||||
|
*/ |
||||
|
const std::string& plot_name() const { return plot_name_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the analysis type (tran, ac, dc, etc.) |
||||
|
*/ |
||||
|
const std::string& analysis_type() const { return analysis_type_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get list of all trace names |
||||
|
* @return Vector of trace names |
||||
|
*/ |
||||
|
std::vector<std::string> get_trace_names() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get a specific trace by name |
||||
|
* @param name Trace name (e.g., "V(out)" or "time") |
||||
|
* @return Shared pointer to Trace object |
||||
|
* @throws std::out_of_range if trace not found |
||||
|
*/ |
||||
|
std::shared_ptr<Trace> get_trace(const std::string& name) const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if a trace exists |
||||
|
* @param name Trace name |
||||
|
*/ |
||||
|
bool has_trace(const std::string& name) const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the number of steps (for parametric sweeps) |
||||
|
* @return List of step indices |
||||
|
*/ |
||||
|
std::vector<int> get_steps() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get step parameter values (for .step simulations) |
||||
|
* |
||||
|
* This is the `steps` property in PyLTSpice. |
||||
|
* Returns a list of dictionaries with parameter names and values. |
||||
|
*/ |
||||
|
const std::vector<std::map<std::string, double>>& steps() const { return step_params_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the number of data points |
||||
|
*/ |
||||
|
size_t num_points() const { return num_points_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the number of variables/traces |
||||
|
*/ |
||||
|
size_t num_variables() const { return traces_.size(); } |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if data is complex (AC analysis) |
||||
|
*/ |
||||
|
bool is_complex() const { return is_complex_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if data is real |
||||
|
*/ |
||||
|
bool is_real() const { return !is_complex_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the raw file path |
||||
|
*/ |
||||
|
const std::string& filepath() const { return filepath_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Parse a raw file |
||||
|
* @param filepath Path to the .raw file |
||||
|
* @return true if parsing succeeded |
||||
|
*/ |
||||
|
bool parse(const std::string& filepath); |
||||
|
|
||||
|
private: |
||||
|
/** |
||||
|
* @brief Parse the header section of the raw file |
||||
|
* @param file Input file stream |
||||
|
* @return true if header was parsed successfully |
||||
|
*/ |
||||
|
bool parse_header(std::istream& file); |
||||
|
|
||||
|
/** |
||||
|
* @brief Parse ASCII data section |
||||
|
* @param file Input file stream |
||||
|
* @return true if data was parsed successfully |
||||
|
*/ |
||||
|
bool parse_ascii_data(std::istream& file); |
||||
|
|
||||
|
/** |
||||
|
* @brief Parse binary data section |
||||
|
* @param file Input file stream |
||||
|
* @return true if data was parsed successfully |
||||
|
*/ |
||||
|
bool parse_binary_data(std::istream& file); |
||||
|
|
||||
|
/** |
||||
|
* @brief Detect step boundaries in the data |
||||
|
*/ |
||||
|
void detect_steps(); |
||||
|
|
||||
|
std::string filepath_; |
||||
|
std::string title_; |
||||
|
std::string date_; |
||||
|
std::string plot_name_; |
||||
|
std::string analysis_type_; |
||||
|
std::string flags_; |
||||
|
|
||||
|
size_t num_points_ = 0; |
||||
|
size_t num_variables_ = 0; |
||||
|
bool is_complex_ = false; |
||||
|
bool is_binary_ = false; |
||||
|
|
||||
|
// Variable info from header |
||||
|
struct VarInfo { |
||||
|
int index; |
||||
|
std::string name; |
||||
|
std::string type; // voltage, current, time, frequency, etc. |
||||
|
}; |
||||
|
std::vector<VarInfo> var_info_; |
||||
|
|
||||
|
// Trace data |
||||
|
std::map<std::string, std::shared_ptr<Trace>> traces_; |
||||
|
|
||||
|
// Step information (for .step simulations) |
||||
|
std::vector<std::map<std::string, double>> step_params_; |
||||
|
std::vector<size_t> step_boundaries_; |
||||
|
}; |
||||
|
|
||||
|
} // namespace ngspice |
||||
|
|
||||
|
#endif // NGSPICE_PYTHON_RAW_READ_H |
||||
@ -0,0 +1,286 @@ |
|||||
|
/**
|
||||
|
* @file sim_runner.cpp |
||||
|
* @brief SimRunner class implementation - PyLTSpice-compatible simulation runner |
||||
|
*/ |
||||
|
|
||||
|
#include "sim_runner.h"
|
||||
|
#include <algorithm>
|
||||
|
#include <filesystem>
|
||||
|
#include <fstream>
|
||||
|
#include <chrono>
|
||||
|
#include <stdexcept>
|
||||
|
|
||||
|
namespace fs = std::filesystem; |
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
// NgSpice static member
|
||||
|
std::vector<std::string> NgSpice::spice_exe; |
||||
|
|
||||
|
std::string NgSpice::version() { |
||||
|
return "ngspice-43"; |
||||
|
} |
||||
|
|
||||
|
// SimRunner implementation
|
||||
|
SimRunner::SimRunner(const std::string& output_folder, void* simulator) |
||||
|
: output_folder_(output_folder) |
||||
|
, simulator_(std::make_unique<Simulator>()) |
||||
|
{ |
||||
|
// Create output directory if it doesn't exist
|
||||
|
if (!output_folder_.empty()) { |
||||
|
fs::create_directories(output_folder_); |
||||
|
} |
||||
|
|
||||
|
// Initialize the simulator
|
||||
|
simulator_->initialize(); |
||||
|
simulator_->set_output_dir(output_folder_); |
||||
|
} |
||||
|
|
||||
|
SimRunner::~SimRunner() { |
||||
|
// Signal workers to shutdown
|
||||
|
shutdown_ = true; |
||||
|
|
||||
|
// Wait for workers to finish
|
||||
|
for (auto& worker : workers_) { |
||||
|
if (worker.joinable()) { |
||||
|
worker.join(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
SimRunner::SimRunner(SimRunner&& other) noexcept |
||||
|
: output_folder_(std::move(other.output_folder_)) |
||||
|
, max_parallel_(other.max_parallel_) |
||||
|
, pending_netlists_(std::move(other.pending_netlists_)) |
||||
|
, completed_results_(std::move(other.completed_results_)) |
||||
|
, simulator_(std::move(other.simulator_)) |
||||
|
, run_counter_(other.run_counter_) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
SimRunner& SimRunner::operator=(SimRunner&& other) noexcept { |
||||
|
if (this != &other) { |
||||
|
output_folder_ = std::move(other.output_folder_); |
||||
|
max_parallel_ = other.max_parallel_; |
||||
|
pending_netlists_ = std::move(other.pending_netlists_); |
||||
|
completed_results_ = std::move(other.completed_results_); |
||||
|
simulator_ = std::move(other.simulator_); |
||||
|
run_counter_ = other.run_counter_; |
||||
|
} |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
std::pair<std::string, std::string> SimRunner::run_now(const std::string& netlist) { |
||||
|
SimulationResult result = run_simulation(netlist); |
||||
|
|
||||
|
if (!result.success) { |
||||
|
throw std::runtime_error("Simulation failed: " + result.error_message); |
||||
|
} |
||||
|
|
||||
|
return {result.raw_file, result.log_file}; |
||||
|
} |
||||
|
|
||||
|
bool SimRunner::run(const std::string& netlist) { |
||||
|
std::lock_guard<std::mutex> lock(queue_mutex_); |
||||
|
pending_netlists_.push(netlist); |
||||
|
|
||||
|
// Start a worker if needed
|
||||
|
// For simplicity, we run synchronously in the background
|
||||
|
// A full implementation would use a thread pool
|
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void SimRunner::wait_completion() { |
||||
|
// Process all pending netlists
|
||||
|
while (has_pending()) { |
||||
|
std::string netlist; |
||||
|
{ |
||||
|
std::lock_guard<std::mutex> lock(queue_mutex_); |
||||
|
if (pending_netlists_.empty()) break; |
||||
|
netlist = pending_netlists_.front(); |
||||
|
pending_netlists_.pop(); |
||||
|
} |
||||
|
|
||||
|
SimulationResult result = run_simulation(netlist); |
||||
|
{ |
||||
|
std::lock_guard<std::mutex> lock(queue_mutex_); |
||||
|
completed_results_.push(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool SimRunner::has_pending() const { |
||||
|
std::lock_guard<std::mutex> lock(queue_mutex_); |
||||
|
return !pending_netlists_.empty(); |
||||
|
} |
||||
|
|
||||
|
size_t SimRunner::pending_count() const { |
||||
|
std::lock_guard<std::mutex> lock(queue_mutex_); |
||||
|
return pending_netlists_.size(); |
||||
|
} |
||||
|
|
||||
|
SimulationResult SimRunner::run_simulation(const std::string& netlist) { |
||||
|
SimulationResult result; |
||||
|
|
||||
|
// Generate output paths
|
||||
|
auto [raw_path, log_path] = generate_output_paths(netlist); |
||||
|
|
||||
|
try { |
||||
|
// Reset simulator for new run
|
||||
|
simulator_->reset(); |
||||
|
simulator_->clear_output(); |
||||
|
|
||||
|
// Load netlist
|
||||
|
if (!simulator_->load_netlist(netlist)) { |
||||
|
result.error_message = "Failed to load netlist: " + netlist; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// Set binary output format
|
||||
|
simulator_->command("set filetype=binary"); |
||||
|
|
||||
|
// Run the simulation
|
||||
|
if (!simulator_->run()) { |
||||
|
result.error_message = "Simulation failed to run"; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// In shared library mode, ngspice does NOT automatically write a .raw file.
|
||||
|
// We must explicitly use the "write" command to dump simulation results.
|
||||
|
// Convert backslashes to forward slashes for ngspice's command parser.
|
||||
|
std::string raw_path_fwd = raw_path; |
||||
|
std::replace(raw_path_fwd.begin(), raw_path_fwd.end(), '\\', '/'); |
||||
|
|
||||
|
std::string write_cmd = "write " + raw_path_fwd; |
||||
|
if (!simulator_->command(write_cmd)) { |
||||
|
result.error_message = "Failed to write raw file: " + raw_path; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
if (!fs::exists(raw_path)) { |
||||
|
// Collect diagnostic info for debugging
|
||||
|
std::string diag = "Raw output file not found after write: " + raw_path; |
||||
|
std::string plot = simulator_->current_plot(); |
||||
|
diag += "\n Current plot: " + (plot.empty() ? "(none)" : plot); |
||||
|
auto vecs = simulator_->all_vectors(plot); |
||||
|
diag += "\n Vector count: " + std::to_string(vecs.size()); |
||||
|
std::string output = simulator_->get_output(); |
||||
|
if (!output.empty()) { |
||||
|
// Include last few lines of simulator output
|
||||
|
diag += "\n Simulator output (last 500 chars): " |
||||
|
+ output.substr(output.size() > 500 ? output.size() - 500 : 0); |
||||
|
} |
||||
|
result.error_message = diag; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
result.raw_file = raw_path; |
||||
|
|
||||
|
// Look for log file (optional)
|
||||
|
fs::path log_candidate = fs::path(result.raw_file).replace_extension(".log"); |
||||
|
if (fs::exists(log_candidate)) { |
||||
|
result.log_file = log_candidate.string(); |
||||
|
} |
||||
|
|
||||
|
// Save simulator output as log if no log file exists
|
||||
|
if (result.log_file.empty()) { |
||||
|
std::string output = simulator_->get_output(); |
||||
|
if (!output.empty()) { |
||||
|
std::ofstream log_out(log_path); |
||||
|
if (log_out.is_open()) { |
||||
|
log_out << output; |
||||
|
result.log_file = log_path; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result.success = true; |
||||
|
|
||||
|
} catch (const std::exception& e) { |
||||
|
result.error_message = e.what(); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
std::pair<std::string, std::string> SimRunner::generate_output_paths( |
||||
|
const std::string& netlist) { |
||||
|
fs::path netlist_path(netlist); |
||||
|
std::string base_name = netlist_path.stem().string(); |
||||
|
|
||||
|
// Add counter suffix to avoid overwriting
|
||||
|
++run_counter_; |
||||
|
std::string suffix = "_" + std::to_string(run_counter_); |
||||
|
|
||||
|
fs::path output_dir = output_folder_.empty() |
||||
|
? netlist_path.parent_path() |
||||
|
: fs::path(output_folder_); |
||||
|
|
||||
|
std::string raw_path = (output_dir / (base_name + suffix + ".raw")).string(); |
||||
|
std::string log_path = (output_dir / (base_name + suffix + ".log")).string(); |
||||
|
|
||||
|
return {raw_path, log_path}; |
||||
|
} |
||||
|
|
||||
|
// Iterator implementation
|
||||
|
SimRunner::Iterator::Iterator(SimRunner* runner) |
||||
|
: runner_(runner) |
||||
|
, at_end_(false) |
||||
|
{ |
||||
|
fetch_next(); |
||||
|
} |
||||
|
|
||||
|
void SimRunner::Iterator::fetch_next() { |
||||
|
if (!runner_) { |
||||
|
at_end_ = true; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Process pending netlists if any
|
||||
|
{ |
||||
|
std::lock_guard<std::mutex> lock(runner_->queue_mutex_); |
||||
|
|
||||
|
// Check for completed results first
|
||||
|
if (!runner_->completed_results_.empty()) { |
||||
|
auto result = runner_->completed_results_.front(); |
||||
|
runner_->completed_results_.pop(); |
||||
|
current_ = {result.raw_file, result.log_file}; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Check for pending netlists
|
||||
|
if (runner_->pending_netlists_.empty()) { |
||||
|
at_end_ = true; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Get next netlist to process
|
||||
|
std::string netlist = runner_->pending_netlists_.front(); |
||||
|
runner_->pending_netlists_.pop(); |
||||
|
|
||||
|
// Need to unlock before running simulation
|
||||
|
lock.~lock_guard(); |
||||
|
|
||||
|
// Run simulation
|
||||
|
SimulationResult result = runner_->run_simulation(netlist); |
||||
|
current_ = {result.raw_file, result.log_file}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
SimRunner::Iterator::reference SimRunner::Iterator::operator*() { |
||||
|
return current_; |
||||
|
} |
||||
|
|
||||
|
SimRunner::Iterator& SimRunner::Iterator::operator++() { |
||||
|
fetch_next(); |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
bool SimRunner::Iterator::operator==(const Iterator& other) const { |
||||
|
if (at_end_ && other.at_end_) return true; |
||||
|
if (at_end_ != other.at_end_) return false; |
||||
|
return runner_ == other.runner_; |
||||
|
} |
||||
|
|
||||
|
} // namespace ngspice
|
||||
@ -0,0 +1,219 @@ |
|||||
|
/** |
||||
|
* @file sim_runner.h |
||||
|
* @brief SimRunner class - PyLTSpice-compatible simulation runner |
||||
|
* |
||||
|
* This class provides a high-level API for running ngspice simulations |
||||
|
* that is compatible with PyLTSpice's SimRunner class. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef NGSPICE_PYTHON_SIM_RUNNER_H |
||||
|
#define NGSPICE_PYTHON_SIM_RUNNER_H |
||||
|
|
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
#include <memory> |
||||
|
#include <queue> |
||||
|
#include <mutex> |
||||
|
#include <thread> |
||||
|
#include <future> |
||||
|
#include "simulator.h" |
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
// Forward declaration |
||||
|
class NgSpiceSimulator; |
||||
|
|
||||
|
/** |
||||
|
* @brief Result of a simulation run |
||||
|
*/ |
||||
|
struct SimulationResult { |
||||
|
std::string raw_file; // Path to .raw output file |
||||
|
std::string log_file; // Path to .log output file (may be empty) |
||||
|
bool success = false; |
||||
|
std::string error_message; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @brief PyLTSpice-compatible simulation runner |
||||
|
* |
||||
|
* Provides methods for running ngspice simulations on netlists |
||||
|
* and retrieving results. Supports both synchronous and asynchronous |
||||
|
* execution patterns. |
||||
|
* |
||||
|
* Usage: |
||||
|
* SimRunner runner("./output"); |
||||
|
* auto [raw, log] = runner.run_now("circuit.net"); |
||||
|
* |
||||
|
* // Or async: |
||||
|
* runner.run("circuit.net"); |
||||
|
* for (auto& result : runner) { |
||||
|
* // process result |
||||
|
* } |
||||
|
*/ |
||||
|
class SimRunner { |
||||
|
public: |
||||
|
/** |
||||
|
* @brief Construct a SimRunner |
||||
|
* @param output_folder Directory for output files |
||||
|
* @param simulator Optional simulator instance (for API compat, ignored) |
||||
|
*/ |
||||
|
explicit SimRunner(const std::string& output_folder = ".", |
||||
|
void* simulator = nullptr); |
||||
|
|
||||
|
/** |
||||
|
* @brief Destructor - waits for any running simulations |
||||
|
*/ |
||||
|
~SimRunner(); |
||||
|
|
||||
|
// Disable copy |
||||
|
SimRunner(const SimRunner&) = delete; |
||||
|
SimRunner& operator=(const SimRunner&) = delete; |
||||
|
|
||||
|
// Allow move |
||||
|
SimRunner(SimRunner&&) noexcept; |
||||
|
SimRunner& operator=(SimRunner&&) noexcept; |
||||
|
|
||||
|
/** |
||||
|
* @brief Run simulation synchronously (blocking) |
||||
|
* @param netlist Path to the netlist file |
||||
|
* @return Pair of (raw_file_path, log_file_path) |
||||
|
* |
||||
|
* This is the primary method for running simulations. |
||||
|
* Returns paths to the output files. |
||||
|
*/ |
||||
|
std::pair<std::string, std::string> run_now(const std::string& netlist); |
||||
|
|
||||
|
/** |
||||
|
* @brief Queue a simulation for async execution |
||||
|
* @param netlist Path to the netlist file |
||||
|
* @return true if simulation was queued |
||||
|
* |
||||
|
* Use the iterator interface to retrieve results. |
||||
|
*/ |
||||
|
bool run(const std::string& netlist); |
||||
|
|
||||
|
/** |
||||
|
* @brief Wait for all queued simulations to complete |
||||
|
*/ |
||||
|
void wait_completion(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if any simulations are pending |
||||
|
*/ |
||||
|
bool has_pending() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the number of pending simulations |
||||
|
*/ |
||||
|
size_t pending_count() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get output folder path |
||||
|
*/ |
||||
|
const std::string& output_folder() const { return output_folder_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Set output folder path |
||||
|
*/ |
||||
|
void set_output_folder(const std::string& folder) { output_folder_ = folder; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Set maximum parallel simulations (default: 1) |
||||
|
*/ |
||||
|
void set_parallel_sims(int count) { max_parallel_ = count; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get maximum parallel simulations |
||||
|
*/ |
||||
|
int parallel_sims() const { return max_parallel_; } |
||||
|
|
||||
|
// Iterator interface for retrieving async results |
||||
|
|
||||
|
/** |
||||
|
* @brief Iterator for simulation results |
||||
|
*/ |
||||
|
class Iterator { |
||||
|
public: |
||||
|
using value_type = std::pair<std::string, std::string>; |
||||
|
using reference = value_type; |
||||
|
using pointer = value_type*; |
||||
|
using iterator_category = std::input_iterator_tag; |
||||
|
using difference_type = std::ptrdiff_t; |
||||
|
|
||||
|
Iterator() : runner_(nullptr), at_end_(true) {} |
||||
|
explicit Iterator(SimRunner* runner); |
||||
|
|
||||
|
reference operator*(); |
||||
|
Iterator& operator++(); |
||||
|
bool operator==(const Iterator& other) const; |
||||
|
bool operator!=(const Iterator& other) const { return !(*this == other); } |
||||
|
|
||||
|
private: |
||||
|
void fetch_next(); |
||||
|
|
||||
|
SimRunner* runner_; |
||||
|
bool at_end_; |
||||
|
std::pair<std::string, std::string> current_; |
||||
|
}; |
||||
|
|
||||
|
Iterator begin() { return Iterator(this); } |
||||
|
Iterator end() { return Iterator(); } |
||||
|
|
||||
|
private: |
||||
|
/** |
||||
|
* @brief Internal method to run a single simulation |
||||
|
*/ |
||||
|
SimulationResult run_simulation(const std::string& netlist); |
||||
|
|
||||
|
/** |
||||
|
* @brief Generate output file paths for a netlist |
||||
|
*/ |
||||
|
std::pair<std::string, std::string> generate_output_paths(const std::string& netlist); |
||||
|
|
||||
|
/** |
||||
|
* @brief Worker thread function for async execution |
||||
|
*/ |
||||
|
void worker_thread(); |
||||
|
|
||||
|
std::string output_folder_; |
||||
|
int max_parallel_ = 1; |
||||
|
|
||||
|
// Async execution support |
||||
|
std::queue<std::string> pending_netlists_; |
||||
|
std::queue<SimulationResult> completed_results_; |
||||
|
mutable std::mutex queue_mutex_; |
||||
|
std::vector<std::thread> workers_; |
||||
|
std::atomic<bool> shutdown_{false}; |
||||
|
|
||||
|
// Simulator instance |
||||
|
std::unique_ptr<Simulator> simulator_; |
||||
|
|
||||
|
// Counter for output file naming |
||||
|
int run_counter_ = 0; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @brief NgSpice simulator class (for PyLTSpice compatibility) |
||||
|
* |
||||
|
* This class exists primarily for API compatibility with PyLTSpice. |
||||
|
* It doesn't do anything special - SimRunner handles the actual simulation. |
||||
|
*/ |
||||
|
class NgSpice { |
||||
|
public: |
||||
|
/** |
||||
|
* @brief Path to ngspice executable (ignored - we use embedded ngspice) |
||||
|
* |
||||
|
* This is provided for API compatibility with PyLTSpice. |
||||
|
* Since we use the embedded ngspice library, this value is ignored. |
||||
|
*/ |
||||
|
static std::vector<std::string> spice_exe; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the ngspice version string |
||||
|
*/ |
||||
|
static std::string version(); |
||||
|
}; |
||||
|
|
||||
|
} // namespace ngspice |
||||
|
|
||||
|
#endif // NGSPICE_PYTHON_SIM_RUNNER_H |
||||
@ -0,0 +1,433 @@ |
|||||
|
/**
|
||||
|
* @file simulator.cpp |
||||
|
* @brief Core ngspice simulator wrapper implementation |
||||
|
*/ |
||||
|
|
||||
|
#include "simulator.h"
|
||||
|
#include "callbacks.h"
|
||||
|
#include <fstream>
|
||||
|
#include <sstream>
|
||||
|
#include <algorithm>
|
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
// Static member initialization
|
||||
|
std::mutex Simulator::instances_mutex_; |
||||
|
std::vector<Simulator*> Simulator::instances_; |
||||
|
|
||||
|
Simulator* Simulator::get_instance(int id) { |
||||
|
std::lock_guard<std::mutex> lock(instances_mutex_); |
||||
|
for (auto* sim : instances_) { |
||||
|
if (sim && sim->instance_id_ == id) { |
||||
|
return sim; |
||||
|
} |
||||
|
} |
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
void Simulator::register_instance(Simulator* sim) { |
||||
|
std::lock_guard<std::mutex> lock(instances_mutex_); |
||||
|
instances_.push_back(sim); |
||||
|
} |
||||
|
|
||||
|
void Simulator::unregister_instance(Simulator* sim) { |
||||
|
std::lock_guard<std::mutex> lock(instances_mutex_); |
||||
|
instances_.erase( |
||||
|
std::remove(instances_.begin(), instances_.end(), sim), |
||||
|
instances_.end() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// Static callback functions
|
||||
|
int Simulator::send_char_callback(char* output, int id, void* user_data) { |
||||
|
if (output) { |
||||
|
auto* sim = static_cast<Simulator*>(user_data); |
||||
|
if (sim) { |
||||
|
sim->handle_output(output); |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int Simulator::send_stat_callback(char* status, int id, void* user_data) { |
||||
|
if (status) { |
||||
|
auto* sim = static_cast<Simulator*>(user_data); |
||||
|
if (sim) { |
||||
|
sim->handle_status(status); |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int Simulator::controlled_exit_callback(int status, int immediate, int quit, |
||||
|
int id, void* user_data) { |
||||
|
auto* sim = static_cast<Simulator*>(user_data); |
||||
|
if (sim) { |
||||
|
sim->handle_exit(status, immediate != 0, quit != 0); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int Simulator::send_data_callback(pvecvaluesall data, int count, int id, |
||||
|
void* user_data) { |
||||
|
auto* sim = static_cast<Simulator*>(user_data); |
||||
|
if (sim && data) { |
||||
|
sim->handle_data(data, count); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int Simulator::send_init_data_callback(pvecinfoall data, int id, void* user_data) { |
||||
|
auto* sim = static_cast<Simulator*>(user_data); |
||||
|
if (sim && data) { |
||||
|
sim->handle_init_data(data); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int Simulator::bg_thread_running_callback(int running, int id, void* user_data) { |
||||
|
auto* sim = static_cast<Simulator*>(user_data); |
||||
|
if (sim) { |
||||
|
sim->handle_thread_status(running != 0); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// Instance callback handlers
|
||||
|
void Simulator::handle_output(const std::string& output) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
accumulated_output_ += output; |
||||
|
if (output_callback_) { |
||||
|
output_callback_(output); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Simulator::handle_status(const std::string& status) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
status_.status_text = status; |
||||
|
|
||||
|
double progress = parse_progress(status); |
||||
|
if (progress >= 0) { |
||||
|
status_.progress = progress; |
||||
|
} |
||||
|
|
||||
|
if (status_callback_) { |
||||
|
status_callback_(status); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Simulator::handle_exit(int status, bool immediate, bool quit) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
running_ = false; |
||||
|
status_.running = false; |
||||
|
status_.completed = true; |
||||
|
if (status != 0) { |
||||
|
status_.error = true; |
||||
|
status_.error_message = "Simulation exited with status " + std::to_string(status); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Simulator::handle_data(pvecvaluesall data, int count) { |
||||
|
// Called during simulation with vector values
|
||||
|
// We don't store this - results are read from raw file
|
||||
|
} |
||||
|
|
||||
|
void Simulator::handle_init_data(pvecinfoall data) { |
||||
|
// Called before simulation with vector info
|
||||
|
// We don't need this - info is in raw file
|
||||
|
} |
||||
|
|
||||
|
void Simulator::handle_thread_status(bool running) { |
||||
|
bg_thread_active_ = running; |
||||
|
if (!running) { |
||||
|
running_ = false; |
||||
|
status_.running = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Constructor / Destructor
|
||||
|
Simulator::Simulator(int instance_id) |
||||
|
: instance_id_(instance_id) |
||||
|
{ |
||||
|
register_instance(this); |
||||
|
} |
||||
|
|
||||
|
Simulator::~Simulator() { |
||||
|
unregister_instance(this); |
||||
|
CallbackManager::instance().unregister_callbacks(instance_id_); |
||||
|
} |
||||
|
|
||||
|
Simulator::Simulator(Simulator&& other) noexcept |
||||
|
: instance_id_(other.instance_id_) |
||||
|
, initialized_(other.initialized_) |
||||
|
, running_(other.running_.load()) |
||||
|
, bg_thread_active_(other.bg_thread_active_.load()) |
||||
|
, output_dir_(std::move(other.output_dir_)) |
||||
|
, accumulated_output_(std::move(other.accumulated_output_)) |
||||
|
, status_(std::move(other.status_)) |
||||
|
, output_callback_(std::move(other.output_callback_)) |
||||
|
, status_callback_(std::move(other.status_callback_)) |
||||
|
{ |
||||
|
other.initialized_ = false; |
||||
|
unregister_instance(&other); |
||||
|
register_instance(this); |
||||
|
} |
||||
|
|
||||
|
Simulator& Simulator::operator=(Simulator&& other) noexcept { |
||||
|
if (this != &other) { |
||||
|
unregister_instance(this); |
||||
|
|
||||
|
instance_id_ = other.instance_id_; |
||||
|
initialized_ = other.initialized_; |
||||
|
running_ = other.running_.load(); |
||||
|
bg_thread_active_ = other.bg_thread_active_.load(); |
||||
|
output_dir_ = std::move(other.output_dir_); |
||||
|
accumulated_output_ = std::move(other.accumulated_output_); |
||||
|
status_ = std::move(other.status_); |
||||
|
output_callback_ = std::move(other.output_callback_); |
||||
|
status_callback_ = std::move(other.status_callback_); |
||||
|
|
||||
|
other.initialized_ = false; |
||||
|
unregister_instance(&other); |
||||
|
register_instance(this); |
||||
|
} |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
bool Simulator::initialize() { |
||||
|
if (initialized_) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// Register callbacks with CallbackManager
|
||||
|
CallbackManager::instance().register_callbacks( |
||||
|
instance_id_, |
||||
|
[this](const std::string& msg) { handle_output(msg); }, |
||||
|
[this](const std::string& status) { handle_status(status); }, |
||||
|
[this](int status, bool immediate, bool quit) { |
||||
|
handle_exit(status, immediate, quit); |
||||
|
}, |
||||
|
[this](bool running) { handle_thread_status(running); } |
||||
|
); |
||||
|
|
||||
|
// Initialize ngspice with callbacks
|
||||
|
int ret = ngSpice_Init( |
||||
|
send_char_callback, |
||||
|
send_stat_callback, |
||||
|
controlled_exit_callback, |
||||
|
send_data_callback, |
||||
|
send_init_data_callback, |
||||
|
bg_thread_running_callback, |
||||
|
this |
||||
|
); |
||||
|
|
||||
|
initialized_ = (ret == 0); |
||||
|
return initialized_; |
||||
|
} |
||||
|
|
||||
|
bool Simulator::load_netlist(const std::string& filepath) { |
||||
|
if (!initialized_ && !initialize()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Clear any previous state
|
||||
|
status_ = SimulationStatus{}; |
||||
|
accumulated_output_.clear(); |
||||
|
|
||||
|
// Use source command to load netlist
|
||||
|
std::string cmd = "source " + filepath; |
||||
|
return command(cmd); |
||||
|
} |
||||
|
|
||||
|
bool Simulator::load_netlist_string(const std::string& netlist_content) { |
||||
|
if (!initialized_ && !initialize()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Split netlist into lines
|
||||
|
std::vector<std::string> lines; |
||||
|
std::istringstream iss(netlist_content); |
||||
|
std::string line; |
||||
|
while (std::getline(iss, line)) { |
||||
|
lines.push_back(line); |
||||
|
} |
||||
|
|
||||
|
// Create null-terminated array of C strings
|
||||
|
std::vector<char*> c_lines; |
||||
|
for (auto& l : lines) { |
||||
|
c_lines.push_back(const_cast<char*>(l.c_str())); |
||||
|
} |
||||
|
c_lines.push_back(nullptr); |
||||
|
|
||||
|
// Clear state
|
||||
|
status_ = SimulationStatus{}; |
||||
|
accumulated_output_.clear(); |
||||
|
|
||||
|
// Load circuit
|
||||
|
int ret = ngSpice_Circ(c_lines.data()); |
||||
|
return ret == 0; |
||||
|
} |
||||
|
|
||||
|
bool Simulator::command(const std::string& cmd) { |
||||
|
if (!initialized_ && !initialize()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
int ret = ngSpice_Command(const_cast<char*>(cmd.c_str())); |
||||
|
return ret == 0; |
||||
|
} |
||||
|
|
||||
|
bool Simulator::run() { |
||||
|
if (!initialized_) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
running_ = true; |
||||
|
status_.running = true; |
||||
|
status_.completed = false; |
||||
|
status_.error = false; |
||||
|
|
||||
|
// Execute run command (blocking)
|
||||
|
bool result = command("run"); |
||||
|
|
||||
|
running_ = false; |
||||
|
status_.running = false; |
||||
|
status_.completed = true; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
bool Simulator::run_async() { |
||||
|
if (!initialized_) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
running_ = true; |
||||
|
status_.running = true; |
||||
|
status_.completed = false; |
||||
|
status_.error = false; |
||||
|
|
||||
|
// Execute bg_run command (non-blocking)
|
||||
|
return command("bg_run"); |
||||
|
} |
||||
|
|
||||
|
bool Simulator::is_running() const { |
||||
|
if (!initialized_) { |
||||
|
return false; |
||||
|
} |
||||
|
return running_ || ngSpice_running() != 0; |
||||
|
} |
||||
|
|
||||
|
void Simulator::halt() { |
||||
|
if (is_running()) { |
||||
|
command("bg_halt"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Simulator::resume() { |
||||
|
command("bg_resume"); |
||||
|
} |
||||
|
|
||||
|
void Simulator::reset() { |
||||
|
command("reset"); |
||||
|
status_ = SimulationStatus{}; |
||||
|
accumulated_output_.clear(); |
||||
|
} |
||||
|
|
||||
|
SimulationStatus Simulator::get_status() const { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
return status_; |
||||
|
} |
||||
|
|
||||
|
std::string Simulator::current_plot() const { |
||||
|
if (!initialized_) { |
||||
|
return ""; |
||||
|
} |
||||
|
char* plot = ngSpice_CurPlot(); |
||||
|
return plot ? plot : ""; |
||||
|
} |
||||
|
|
||||
|
std::vector<std::string> Simulator::all_plots() const { |
||||
|
std::vector<std::string> result; |
||||
|
if (!initialized_) { |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
char** plots = ngSpice_AllPlots(); |
||||
|
if (plots) { |
||||
|
for (int i = 0; plots[i] != nullptr; ++i) { |
||||
|
result.push_back(plots[i]); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
std::vector<std::string> Simulator::all_vectors(const std::string& plot_name) const { |
||||
|
std::vector<std::string> result; |
||||
|
if (!initialized_) { |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
std::string plot = plot_name.empty() ? current_plot() : plot_name; |
||||
|
if (plot.empty()) { |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
char** vecs = ngSpice_AllVecs(const_cast<char*>(plot.c_str())); |
||||
|
if (vecs) { |
||||
|
for (int i = 0; vecs[i] != nullptr; ++i) { |
||||
|
result.push_back(vecs[i]); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
VectorInfo Simulator::get_vector_info(const std::string& vec_name) const { |
||||
|
VectorInfo info; |
||||
|
if (!initialized_) { |
||||
|
return info; |
||||
|
} |
||||
|
|
||||
|
pvector_info vi = ngGet_Vec_Info(const_cast<char*>(vec_name.c_str())); |
||||
|
if (!vi) { |
||||
|
return info; |
||||
|
} |
||||
|
|
||||
|
info.name = vi->v_name ? vi->v_name : ""; |
||||
|
info.type = vi->v_type; |
||||
|
info.length = vi->v_length; |
||||
|
info.is_real = (vi->v_compdata == nullptr); |
||||
|
info.is_complex = !info.is_real; |
||||
|
|
||||
|
// Copy data
|
||||
|
if (vi->v_realdata && vi->v_length > 0) { |
||||
|
info.real_data.assign(vi->v_realdata, vi->v_realdata + vi->v_length); |
||||
|
} |
||||
|
|
||||
|
// Note: Complex data would need special handling
|
||||
|
// vi->v_compdata is ngcomplex_t* which is {double cx_real, cx_imag}
|
||||
|
|
||||
|
return info; |
||||
|
} |
||||
|
|
||||
|
void Simulator::set_output_callback(std::function<void(const std::string&)> callback) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
output_callback_ = std::move(callback); |
||||
|
} |
||||
|
|
||||
|
void Simulator::set_status_callback(std::function<void(const std::string&)> callback) { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
status_callback_ = std::move(callback); |
||||
|
} |
||||
|
|
||||
|
std::string Simulator::get_output() const { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
return accumulated_output_; |
||||
|
} |
||||
|
|
||||
|
void Simulator::clear_output() { |
||||
|
std::lock_guard<std::mutex> lock(mutex_); |
||||
|
accumulated_output_.clear(); |
||||
|
} |
||||
|
|
||||
|
} // namespace ngspice
|
||||
@ -0,0 +1,298 @@ |
|||||
|
/** |
||||
|
* @file simulator.h |
||||
|
* @brief Core ngspice simulator wrapper for Python extension |
||||
|
* |
||||
|
* This class wraps the ngspice shared library API to provide |
||||
|
* circuit simulation capabilities to Python. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef NGSPICE_PYTHON_SIMULATOR_H |
||||
|
#define NGSPICE_PYTHON_SIMULATOR_H |
||||
|
|
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
#include <functional> |
||||
|
#include <memory> |
||||
|
#include <mutex> |
||||
|
#include <atomic> |
||||
|
|
||||
|
// Forward declarations for ngspice types |
||||
|
extern "C" { |
||||
|
// ngspice shared library API types |
||||
|
typedef struct vecvalues { |
||||
|
char* name; |
||||
|
double creal; |
||||
|
double cimag; |
||||
|
int is_scale; |
||||
|
int is_complex; |
||||
|
} vecvalues, *pvecvalues; |
||||
|
|
||||
|
typedef struct vecvaluesall { |
||||
|
int veccount; |
||||
|
int vecindex; |
||||
|
pvecvalues *vecsa; |
||||
|
} vecvaluesall, *pvecvaluesall; |
||||
|
|
||||
|
typedef struct vecinfo { |
||||
|
int number; |
||||
|
char *vecname; |
||||
|
int is_real; |
||||
|
void *pdvec; |
||||
|
void *pdvecscale; |
||||
|
} vecinfo, *pvecinfo; |
||||
|
|
||||
|
typedef struct vecinfoall { |
||||
|
char *name; |
||||
|
char *title; |
||||
|
char *date; |
||||
|
char *type; |
||||
|
int veccount; |
||||
|
pvecinfo *vecs; |
||||
|
} vecinfoall, *pvecinfoall; |
||||
|
|
||||
|
typedef struct vector_info { |
||||
|
char *v_name; |
||||
|
int v_type; |
||||
|
short v_flags; |
||||
|
double *v_realdata; |
||||
|
void *v_compdata; // ngcomplex_t* |
||||
|
int v_length; |
||||
|
} vector_info, *pvector_info; |
||||
|
|
||||
|
// Callback function types |
||||
|
typedef int (SendChar)(char*, int, void*); |
||||
|
typedef int (SendStat)(char*, int, void*); |
||||
|
typedef int (ControlledExit)(int, int, int, int, void*); |
||||
|
typedef int (SendData)(pvecvaluesall, int, int, void*); |
||||
|
typedef int (SendInitData)(pvecinfoall, int, void*); |
||||
|
typedef int (BGThreadRunning)(int, int, void*); |
||||
|
|
||||
|
// ngspice API functions |
||||
|
int ngSpice_Init(SendChar*, SendStat*, ControlledExit*, |
||||
|
SendData*, SendInitData*, BGThreadRunning*, void*); |
||||
|
int ngSpice_Command(char*); |
||||
|
int ngSpice_Circ(char**); |
||||
|
char* ngSpice_CurPlot(void); |
||||
|
char** ngSpice_AllPlots(void); |
||||
|
char** ngSpice_AllVecs(char*); |
||||
|
pvector_info ngGet_Vec_Info(char*); |
||||
|
int ngSpice_running(void); |
||||
|
} |
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
/** |
||||
|
* @brief Simulation status information |
||||
|
*/ |
||||
|
struct SimulationStatus { |
||||
|
bool running = false; |
||||
|
bool completed = false; |
||||
|
bool error = false; |
||||
|
std::string error_message; |
||||
|
double progress = 0.0; // 0.0 to 1.0 |
||||
|
std::string status_text; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @brief Vector/trace information |
||||
|
*/ |
||||
|
struct VectorInfo { |
||||
|
std::string name; |
||||
|
int type; |
||||
|
bool is_real; |
||||
|
bool is_complex; |
||||
|
int length; |
||||
|
std::vector<double> real_data; |
||||
|
std::vector<double> imag_data; // Only for complex vectors |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @brief Core ngspice simulator wrapper |
||||
|
* |
||||
|
* This class manages the ngspice simulation engine lifecycle, |
||||
|
* handles callbacks, and provides methods for loading netlists |
||||
|
* and running simulations. |
||||
|
*/ |
||||
|
class Simulator { |
||||
|
public: |
||||
|
/** |
||||
|
* @brief Construct a new Simulator instance |
||||
|
* @param instance_id Optional ID for parallel simulation instances |
||||
|
*/ |
||||
|
explicit Simulator(int instance_id = 0); |
||||
|
|
||||
|
/** |
||||
|
* @brief Destroy the Simulator instance |
||||
|
*/ |
||||
|
~Simulator(); |
||||
|
|
||||
|
// Disable copy (ngspice has global state) |
||||
|
Simulator(const Simulator&) = delete; |
||||
|
Simulator& operator=(const Simulator&) = delete; |
||||
|
|
||||
|
// Allow move |
||||
|
Simulator(Simulator&&) noexcept; |
||||
|
Simulator& operator=(Simulator&&) noexcept; |
||||
|
|
||||
|
/** |
||||
|
* @brief Initialize the ngspice engine |
||||
|
* @return true if initialization succeeded |
||||
|
*/ |
||||
|
bool initialize(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if simulator is initialized |
||||
|
*/ |
||||
|
bool is_initialized() const { return initialized_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Load a netlist from file |
||||
|
* @param filepath Path to the .net/.cir file |
||||
|
* @return true if loading succeeded |
||||
|
*/ |
||||
|
bool load_netlist(const std::string& filepath); |
||||
|
|
||||
|
/** |
||||
|
* @brief Load a netlist from string content |
||||
|
* @param netlist_content The netlist as a string |
||||
|
* @return true if loading succeeded |
||||
|
*/ |
||||
|
bool load_netlist_string(const std::string& netlist_content); |
||||
|
|
||||
|
/** |
||||
|
* @brief Execute a SPICE command |
||||
|
* @param command The command to execute (e.g., "run", "op", "ac ...") |
||||
|
* @return true if command executed successfully |
||||
|
*/ |
||||
|
bool command(const std::string& command); |
||||
|
|
||||
|
/** |
||||
|
* @brief Run the simulation (blocking) |
||||
|
* @return true if simulation completed successfully |
||||
|
*/ |
||||
|
bool run(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Run simulation in background thread |
||||
|
* @return true if background simulation started |
||||
|
*/ |
||||
|
bool run_async(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if simulation is currently running |
||||
|
*/ |
||||
|
bool is_running() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Halt a running simulation |
||||
|
*/ |
||||
|
void halt(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Resume a halted simulation |
||||
|
*/ |
||||
|
void resume(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Reset the simulator state |
||||
|
*/ |
||||
|
void reset(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Get current simulation status |
||||
|
*/ |
||||
|
SimulationStatus get_status() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the current plot name |
||||
|
*/ |
||||
|
std::string current_plot() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get all plot names |
||||
|
*/ |
||||
|
std::vector<std::string> all_plots() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get all vector names in a plot |
||||
|
* @param plot_name Name of the plot (or empty for current) |
||||
|
*/ |
||||
|
std::vector<std::string> all_vectors(const std::string& plot_name = "") const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get vector/trace information |
||||
|
* @param vec_name Full vector name (e.g., "tran1.V(out)") |
||||
|
*/ |
||||
|
VectorInfo get_vector_info(const std::string& vec_name) const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Set callback for stdout/stderr output |
||||
|
*/ |
||||
|
void set_output_callback(std::function<void(const std::string&)> callback); |
||||
|
|
||||
|
/** |
||||
|
* @brief Set callback for status updates |
||||
|
*/ |
||||
|
void set_status_callback(std::function<void(const std::string&)> callback); |
||||
|
|
||||
|
/** |
||||
|
* @brief Get accumulated output messages |
||||
|
*/ |
||||
|
std::string get_output() const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Clear accumulated output |
||||
|
*/ |
||||
|
void clear_output(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the output directory for raw files |
||||
|
*/ |
||||
|
std::string get_output_dir() const { return output_dir_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Set the output directory for raw files |
||||
|
*/ |
||||
|
void set_output_dir(const std::string& dir) { output_dir_ = dir; } |
||||
|
|
||||
|
private: |
||||
|
// Static callback functions (registered with ngspice) |
||||
|
static int send_char_callback(char* output, int id, void* user_data); |
||||
|
static int send_stat_callback(char* status, int id, void* user_data); |
||||
|
static int controlled_exit_callback(int status, int immediate, int quit, int id, void* user_data); |
||||
|
static int send_data_callback(pvecvaluesall data, int count, int id, void* user_data); |
||||
|
static int send_init_data_callback(pvecinfoall data, int id, void* user_data); |
||||
|
static int bg_thread_running_callback(int running, int id, void* user_data); |
||||
|
|
||||
|
// Instance callback handlers |
||||
|
void handle_output(const std::string& output); |
||||
|
void handle_status(const std::string& status); |
||||
|
void handle_exit(int status, bool immediate, bool quit); |
||||
|
void handle_data(pvecvaluesall data, int count); |
||||
|
void handle_init_data(pvecinfoall data); |
||||
|
void handle_thread_status(bool running); |
||||
|
|
||||
|
int instance_id_; |
||||
|
bool initialized_ = false; |
||||
|
std::atomic<bool> running_{false}; |
||||
|
std::atomic<bool> bg_thread_active_{false}; |
||||
|
mutable std::mutex mutex_; |
||||
|
|
||||
|
std::string output_dir_; |
||||
|
std::string accumulated_output_; |
||||
|
SimulationStatus status_; |
||||
|
|
||||
|
std::function<void(const std::string&)> output_callback_; |
||||
|
std::function<void(const std::string&)> status_callback_; |
||||
|
|
||||
|
// Global instance map for callback routing |
||||
|
static std::mutex instances_mutex_; |
||||
|
static std::vector<Simulator*> instances_; |
||||
|
static Simulator* get_instance(int id); |
||||
|
static void register_instance(Simulator* sim); |
||||
|
static void unregister_instance(Simulator* sim); |
||||
|
}; |
||||
|
|
||||
|
} // namespace ngspice |
||||
|
|
||||
|
#endif // NGSPICE_PYTHON_SIMULATOR_H |
||||
@ -0,0 +1,130 @@ |
|||||
|
/**
|
||||
|
* @file trace.cpp |
||||
|
* @brief Trace class implementation |
||||
|
*/ |
||||
|
|
||||
|
#include "trace.h"
|
||||
|
#include <stdexcept>
|
||||
|
#include <algorithm>
|
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
Trace::Trace(const std::string& name, bool is_complex) |
||||
|
: name_(name) |
||||
|
, is_complex_(is_complex) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
std::vector<double> Trace::get_wave(size_t step) const { |
||||
|
if (step_offsets_.empty()) { |
||||
|
// No step boundaries - return all data
|
||||
|
return real_data_; |
||||
|
} |
||||
|
|
||||
|
if (step >= step_offsets_.size()) { |
||||
|
throw std::out_of_range("Step index " + std::to_string(step) + |
||||
|
" out of range (max: " + |
||||
|
std::to_string(step_offsets_.size() - 1) + ")"); |
||||
|
} |
||||
|
|
||||
|
size_t start = step_offsets_[step]; |
||||
|
size_t end = (step + 1 < step_offsets_.size()) |
||||
|
? step_offsets_[step + 1] |
||||
|
: real_data_.size(); |
||||
|
|
||||
|
return std::vector<double>(real_data_.begin() + start, |
||||
|
real_data_.begin() + end); |
||||
|
} |
||||
|
|
||||
|
std::vector<std::complex<double>> Trace::get_wave_complex(size_t step) const { |
||||
|
if (!is_complex_) { |
||||
|
// Convert real data to complex
|
||||
|
auto real = get_wave(step); |
||||
|
std::vector<std::complex<double>> result; |
||||
|
result.reserve(real.size()); |
||||
|
for (double v : real) { |
||||
|
result.emplace_back(v, 0.0); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
if (step_offsets_.empty()) { |
||||
|
// No step boundaries - return all data
|
||||
|
std::vector<std::complex<double>> result; |
||||
|
result.reserve(real_data_.size()); |
||||
|
for (size_t i = 0; i < real_data_.size(); ++i) { |
||||
|
result.emplace_back(real_data_[i], |
||||
|
i < imag_data_.size() ? imag_data_[i] : 0.0); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
if (step >= step_offsets_.size()) { |
||||
|
throw std::out_of_range("Step index " + std::to_string(step) + |
||||
|
" out of range"); |
||||
|
} |
||||
|
|
||||
|
size_t start = step_offsets_[step]; |
||||
|
size_t end = (step + 1 < step_offsets_.size()) |
||||
|
? step_offsets_[step + 1] |
||||
|
: real_data_.size(); |
||||
|
|
||||
|
std::vector<std::complex<double>> result; |
||||
|
result.reserve(end - start); |
||||
|
for (size_t i = start; i < end; ++i) { |
||||
|
result.emplace_back(real_data_[i], |
||||
|
i < imag_data_.size() ? imag_data_[i] : 0.0); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
size_t Trace::step_size(size_t step) const { |
||||
|
if (step_offsets_.empty()) { |
||||
|
return real_data_.size(); |
||||
|
} |
||||
|
|
||||
|
if (step >= step_offsets_.size()) { |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
size_t start = step_offsets_[step]; |
||||
|
size_t end = (step + 1 < step_offsets_.size()) |
||||
|
? step_offsets_[step + 1] |
||||
|
: real_data_.size(); |
||||
|
|
||||
|
return end - start; |
||||
|
} |
||||
|
|
||||
|
void Trace::reserve(size_t count) { |
||||
|
real_data_.reserve(count); |
||||
|
if (is_complex_) { |
||||
|
imag_data_.reserve(count); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Trace::add_point(double value) { |
||||
|
real_data_.push_back(value); |
||||
|
} |
||||
|
|
||||
|
void Trace::add_point(double real, double imag) { |
||||
|
real_data_.push_back(real); |
||||
|
imag_data_.push_back(imag); |
||||
|
is_complex_ = true; |
||||
|
} |
||||
|
|
||||
|
void Trace::mark_step_boundary() { |
||||
|
step_offsets_.push_back(real_data_.size()); |
||||
|
} |
||||
|
|
||||
|
void Trace::set_data(std::vector<double> real_data, |
||||
|
std::vector<double> imag_data) { |
||||
|
real_data_ = std::move(real_data); |
||||
|
imag_data_ = std::move(imag_data); |
||||
|
is_complex_ = !imag_data_.empty(); |
||||
|
} |
||||
|
|
||||
|
void Trace::set_step_offsets(std::vector<size_t> offsets) { |
||||
|
step_offsets_ = std::move(offsets); |
||||
|
} |
||||
|
|
||||
|
} // namespace ngspice
|
||||
@ -0,0 +1,129 @@ |
|||||
|
/** |
||||
|
* @file trace.h |
||||
|
* @brief Trace class for waveform data access |
||||
|
* |
||||
|
* The Trace class represents a single simulation vector/waveform |
||||
|
* and provides access to the data as numpy arrays. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef NGSPICE_PYTHON_TRACE_H |
||||
|
#define NGSPICE_PYTHON_TRACE_H |
||||
|
|
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
#include <complex> |
||||
|
#include <memory> |
||||
|
|
||||
|
namespace ngspice { |
||||
|
|
||||
|
/** |
||||
|
* @brief Represents a single trace/vector from simulation results |
||||
|
* |
||||
|
* This class holds waveform data and provides methods compatible |
||||
|
* with PyLTSpice's Trace class. |
||||
|
*/ |
||||
|
class Trace { |
||||
|
public: |
||||
|
/** |
||||
|
* @brief Construct a Trace |
||||
|
* @param name The trace name (e.g., "V(out)") |
||||
|
* @param is_complex Whether the data is complex |
||||
|
*/ |
||||
|
Trace(const std::string& name, bool is_complex = false); |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the trace name |
||||
|
*/ |
||||
|
const std::string& name() const { return name_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Check if trace contains complex data |
||||
|
*/ |
||||
|
bool is_complex() const { return is_complex_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get the number of steps (for parametric sweeps) |
||||
|
*/ |
||||
|
size_t num_steps() const { return step_offsets_.empty() ? 1 : step_offsets_.size(); } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get waveform data for a specific step |
||||
|
* @param step Step index (0 for single-step simulations) |
||||
|
* @return Vector of data points |
||||
|
* |
||||
|
* For PyLTSpice compatibility, this returns real values. |
||||
|
* Use get_wave_complex() for complex data. |
||||
|
*/ |
||||
|
std::vector<double> get_wave(size_t step = 0) const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get complex waveform data for a specific step |
||||
|
* @param step Step index (0 for single-step simulations) |
||||
|
* @return Vector of complex data points |
||||
|
*/ |
||||
|
std::vector<std::complex<double>> get_wave_complex(size_t step = 0) const; |
||||
|
|
||||
|
/** |
||||
|
* @brief Get all data points (all steps concatenated) |
||||
|
*/ |
||||
|
const std::vector<double>& get_all_real_data() const { return real_data_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get all imaginary data points |
||||
|
*/ |
||||
|
const std::vector<double>& get_all_imag_data() const { return imag_data_; } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get total number of data points |
||||
|
*/ |
||||
|
size_t size() const { return real_data_.size(); } |
||||
|
|
||||
|
/** |
||||
|
* @brief Get number of data points for a specific step |
||||
|
*/ |
||||
|
size_t step_size(size_t step = 0) const; |
||||
|
|
||||
|
// Data loading methods (used during raw file parsing) |
||||
|
|
||||
|
/** |
||||
|
* @brief Reserve space for data |
||||
|
*/ |
||||
|
void reserve(size_t count); |
||||
|
|
||||
|
/** |
||||
|
* @brief Add a real data point |
||||
|
*/ |
||||
|
void add_point(double value); |
||||
|
|
||||
|
/** |
||||
|
* @brief Add a complex data point |
||||
|
*/ |
||||
|
void add_point(double real, double imag); |
||||
|
|
||||
|
/** |
||||
|
* @brief Mark the start of a new step |
||||
|
*/ |
||||
|
void mark_step_boundary(); |
||||
|
|
||||
|
/** |
||||
|
* @brief Set data directly (for efficiency) |
||||
|
*/ |
||||
|
void set_data(std::vector<double> real_data, |
||||
|
std::vector<double> imag_data = {}); |
||||
|
|
||||
|
/** |
||||
|
* @brief Set step offsets directly |
||||
|
*/ |
||||
|
void set_step_offsets(std::vector<size_t> offsets); |
||||
|
|
||||
|
private: |
||||
|
std::string name_; |
||||
|
bool is_complex_; |
||||
|
std::vector<double> real_data_; |
||||
|
std::vector<double> imag_data_; |
||||
|
std::vector<size_t> step_offsets_; // Index where each step starts |
||||
|
}; |
||||
|
|
||||
|
} // namespace ngspice |
||||
|
|
||||
|
#endif // NGSPICE_PYTHON_TRACE_H |
||||
@ -0,0 +1,69 @@ |
|||||
|
"""Shared test fixtures for pyngspice tests.""" |
||||
|
|
||||
|
import os |
||||
|
import sys |
||||
|
import tempfile |
||||
|
|
||||
|
import pytest |
||||
|
|
||||
|
# Ensure repo root is on path for editable installs |
||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||
|
|
||||
|
|
||||
|
@pytest.fixture |
||||
|
def tmp_workdir(): |
||||
|
"""Provide a temporary working directory, cleaned up after the test.""" |
||||
|
with tempfile.TemporaryDirectory() as d: |
||||
|
yield d |
||||
|
|
||||
|
|
||||
|
@pytest.fixture |
||||
|
def rc_netlist(tmp_workdir): |
||||
|
"""Write a simple RC circuit netlist and return its path.""" |
||||
|
path = os.path.join(tmp_workdir, "rc.cir") |
||||
|
with open(path, "w") as f: |
||||
|
f.write("""RC Test Circuit |
||||
|
V1 in 0 DC 1 |
||||
|
R1 in out 1k |
||||
|
C1 out 0 1u |
||||
|
.tran 0.1m 10m |
||||
|
.end |
||||
|
""") |
||||
|
return path |
||||
|
|
||||
|
|
||||
|
@pytest.fixture |
||||
|
def inductor_rser_netlist(tmp_workdir): |
||||
|
"""Write a netlist with LTspice-style Rser= on inductors.""" |
||||
|
path = os.path.join(tmp_workdir, "inductor_rser.cir") |
||||
|
with open(path, "w") as f: |
||||
|
f.write("""Tesla Coil Primary |
||||
|
V1 drive 0 SIN(0 100 250000) |
||||
|
L1 drive tank 8.5u Rser=0.012 |
||||
|
C1 tank 0 100n |
||||
|
L2 tank sec 500u Rser=2.5 |
||||
|
R_load sec 0 50 |
||||
|
.tran 0.1u 100u |
||||
|
.backanno |
||||
|
.end |
||||
|
""") |
||||
|
return path |
||||
|
|
||||
|
|
||||
|
@pytest.fixture |
||||
|
def tesla_coil_netlist(tmp_workdir): |
||||
|
"""Write a Tesla coil netlist with .options savecurrents and Rser=.""" |
||||
|
path = os.path.join(tmp_workdir, "tesla_coil.cir") |
||||
|
with open(path, "w") as f: |
||||
|
f.write("""Tesla Coil AC Analysis |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
L1 p1 0 10.927u Rser=0.001 |
||||
|
L2 0 top 15.987m Rser=0.001 |
||||
|
C_topload top 0 13.822p |
||||
|
K1 L1 L2 0.3204 |
||||
|
.options savecurrents |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""") |
||||
|
return path |
||||
@ -0,0 +1,817 @@ |
|||||
|
"""Tests for the netlist pre-processor (LTspice -> ngspice translation).""" |
||||
|
|
||||
|
import pytest |
||||
|
from pyngspice.netlist import ( |
||||
|
preprocess_netlist, _process_inductor_rser, |
||||
|
_collect_component_names, _is_savecurrents_option, |
||||
|
_process_capacitor_probe, _parse_save_cap_currents, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
class TestInductorRser: |
||||
|
"""Test Rser= parameter extraction from inductor lines.""" |
||||
|
|
||||
|
def test_basic_rser(self): |
||||
|
netlist = """Tesla Coil |
||||
|
L1 p1 0 8.5u Rser=0.012 |
||||
|
C1 p1 0 100n |
||||
|
.tran 1u 100u |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "L1 p1 _rser_L1 8.5u" in result |
||||
|
assert "R_L1_ser _rser_L1 0 0.012" in result |
||||
|
assert "Rser" not in result |
||||
|
|
||||
|
def test_rser_with_suffix(self): |
||||
|
"""Rser value with engineering suffix (12m = 12 milliohms).""" |
||||
|
netlist = """Test |
||||
|
L1 a b 10u Rser=12m |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "L1 a _rser_L1 10u" in result |
||||
|
assert "R_L1_ser _rser_L1 b 12m" in result |
||||
|
|
||||
|
def test_rser_scientific_notation(self): |
||||
|
"""Rser value in scientific notation.""" |
||||
|
netlist = """Test |
||||
|
L1 a b 1.5e-6 Rser=1.2e-3 |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "R_L1_ser" in result |
||||
|
assert "1.2e-3" in result |
||||
|
|
||||
|
def test_multiple_inductors(self): |
||||
|
"""Multiple inductors with Rser= in same netlist.""" |
||||
|
netlist = """Multi Inductor |
||||
|
L1 a 0 10u Rser=0.01 |
||||
|
L2 b 0 20u Rser=0.02 |
||||
|
L3 c 0 30u Rser=0.03 |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "R_L1_ser" in result |
||||
|
assert "R_L2_ser" in result |
||||
|
assert "R_L3_ser" in result |
||||
|
assert "_rser_L1" in result |
||||
|
assert "_rser_L2" in result |
||||
|
assert "_rser_L3" in result |
||||
|
|
||||
|
def test_named_inductor(self): |
||||
|
"""Inductor with alphanumeric name (Lprimary).""" |
||||
|
netlist = """Test |
||||
|
Lprimary drive tank 8.5u Rser=0.012 |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "Lprimary drive _rser_Lprimary 8.5u" in result |
||||
|
assert "R_Lprimary_ser _rser_Lprimary tank 0.012" in result |
||||
|
|
||||
|
def test_inductor_without_rser_unchanged(self): |
||||
|
"""Inductors without Rser= should pass through unchanged.""" |
||||
|
netlist = """Test |
||||
|
L1 a b 10u |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "L1 a b 10u" in result |
||||
|
assert "R_L1" not in result |
||||
|
|
||||
|
def test_rser_with_spaces(self): |
||||
|
"""Rser with spaces around equals sign.""" |
||||
|
netlist = """Test |
||||
|
L1 a b 10u Rser = 0.05 |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "R_L1_ser" in result |
||||
|
assert "0.05" in result |
||||
|
|
||||
|
def test_resistors_before_end(self): |
||||
|
"""Extra resistor lines should be inserted before .end.""" |
||||
|
netlist = """Test |
||||
|
L1 a 0 10u Rser=0.01 |
||||
|
.tran 1u 100u |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
lines = result.splitlines() |
||||
|
# Find positions |
||||
|
rser_idx = next(i for i, l in enumerate(lines) if "R_L1_ser" in l) |
||||
|
end_idx = next(i for i, l in enumerate(lines) if l.strip().lower() == '.end') |
||||
|
assert rser_idx < end_idx |
||||
|
|
||||
|
|
||||
|
class TestBackanno: |
||||
|
"""Test .backanno directive stripping.""" |
||||
|
|
||||
|
def test_backanno_removed(self): |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
.backanno |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert ".backanno" not in result |
||||
|
assert ".end" in result |
||||
|
assert "V1 in 0 1" in result |
||||
|
|
||||
|
def test_backanno_case_insensitive(self): |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
.BACKANNO |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert ".BACKANNO" not in result |
||||
|
|
||||
|
|
||||
|
class TestPassthrough: |
||||
|
"""Test that standard SPICE constructs pass through unchanged.""" |
||||
|
|
||||
|
def test_simple_netlist_unchanged(self): |
||||
|
netlist = """Simple RC |
||||
|
V1 in 0 DC 1 |
||||
|
R1 in out 1k |
||||
|
C1 out 0 1u |
||||
|
.tran 0.1m 10m |
||||
|
.end""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert result == netlist |
||||
|
|
||||
|
def test_behavioral_source_unchanged(self): |
||||
|
"""Behavioral sources should pass through.""" |
||||
|
netlist = """Test |
||||
|
B2 N015 0 V=I(L1)*{iscale} |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "B2 N015 0 V=I(L1)*{iscale}" in result |
||||
|
|
||||
|
def test_subcircuit_unchanged(self): |
||||
|
"""Subcircuit definitions should pass through.""" |
||||
|
netlist = """Test |
||||
|
.subckt mycomp in out |
||||
|
R1 in out 1k |
||||
|
.ends mycomp |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert ".subckt mycomp in out" in result |
||||
|
assert ".ends mycomp" in result |
||||
|
|
||||
|
def test_continuation_lines(self): |
||||
|
"""Continuation lines (starting with +) should pass through.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 PULSE(0 5 0 1n 1n |
||||
|
+ 500n 1u) |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "+ 500n 1u)" in result |
||||
|
|
||||
|
def test_empty_lines_preserved(self): |
||||
|
"""Empty lines should be preserved.""" |
||||
|
netlist = """Test |
||||
|
|
||||
|
V1 in 0 1 |
||||
|
|
||||
|
R1 in out 1k |
||||
|
|
||||
|
.end""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "\n\n" in result |
||||
|
|
||||
|
|
||||
|
class TestProcessInductorRser: |
||||
|
"""Test the internal _process_inductor_rser function directly.""" |
||||
|
|
||||
|
def test_basic(self): |
||||
|
line, extras = _process_inductor_rser("L1 p1 0 8.5u Rser=0.012") |
||||
|
assert line == "L1 p1 _rser_L1 8.5u" |
||||
|
assert len(extras) == 1 |
||||
|
assert extras[0] == "R_L1_ser _rser_L1 0 0.012" |
||||
|
|
||||
|
def test_no_rser_returns_original(self): |
||||
|
line, extras = _process_inductor_rser("L1 p1 0 8.5u") |
||||
|
assert line == "L1 p1 0 8.5u" |
||||
|
assert extras == [] |
||||
|
|
||||
|
def test_preserves_remaining_params(self): |
||||
|
"""Parameters after Rser= that are not LTspice-specific should be kept.""" |
||||
|
line, extras = _process_inductor_rser("L1 a b 10u Rser=0.01 IC=0.5") |
||||
|
assert "IC=0.5" in line |
||||
|
assert len(extras) == 1 |
||||
|
|
||||
|
|
||||
|
class TestSaveCurrents: |
||||
|
"""Test .options savecurrents expansion into explicit .save directives.""" |
||||
|
|
||||
|
def test_savecurrents_expanded(self): |
||||
|
"""`.options savecurrents` should be removed and .save directives added.""" |
||||
|
netlist = """Tesla Coil |
||||
|
V1 vin 0 AC 1 |
||||
|
C1 vin out 0.03u |
||||
|
R1 out 0 50 |
||||
|
.options savecurrents |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert ".options savecurrents" not in result.lower() |
||||
|
assert ".save all" in result |
||||
|
assert "i(V1)" in result |
||||
|
assert "i(C1)" in result |
||||
|
assert "i(R1)" in result |
||||
|
|
||||
|
def test_savecurrents_includes_all_components(self): |
||||
|
"""All R, C, L, V components should get i(name) saves.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 DC 1 |
||||
|
R1 in mid 1k |
||||
|
L1 mid out 10u |
||||
|
C1 out 0 1u |
||||
|
.options savecurrents |
||||
|
.tran 1u 100u |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "i(V1)" in result |
||||
|
assert "i(R1)" in result |
||||
|
assert "i(L1)" in result |
||||
|
assert "i(C1)" in result |
||||
|
|
||||
|
def test_savecurrents_skips_K_elements(self): |
||||
|
"""K (coupling) elements should NOT get i(K1) saves.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
L1 in 0 10u |
||||
|
L2 0 out 20u |
||||
|
K1 L1 L2 0.5 |
||||
|
.options savecurrents |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "i(K1)" not in result |
||||
|
assert "i(V1)" in result |
||||
|
assert "i(L1)" in result |
||||
|
assert "i(L2)" in result |
||||
|
|
||||
|
def test_savecurrents_includes_rser_resistors(self): |
||||
|
"""Expanded Rser resistors should be included in .save directives.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
L1 in 0 10u Rser=0.01 |
||||
|
.options savecurrents |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert "i(R_L1_ser)" in result |
||||
|
assert "i(L1)" in result |
||||
|
assert "i(V1)" in result |
||||
|
|
||||
|
def test_savecurrents_case_insensitive(self): |
||||
|
""".OPTIONS SAVECURRENTS should be handled.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
R1 in 0 1k |
||||
|
.OPTIONS SAVECURRENTS |
||||
|
.op |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert ".OPTIONS SAVECURRENTS" not in result |
||||
|
assert ".save all" in result |
||||
|
assert "i(V1)" in result |
||||
|
|
||||
|
def test_no_savecurrents_unchanged(self): |
||||
|
"""Netlists without .options savecurrents should not get .save directives.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 DC 1 |
||||
|
R1 in out 1k |
||||
|
C1 out 0 1u |
||||
|
.tran 0.1m 10m |
||||
|
.end""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert ".save" not in result.lower() |
||||
|
assert result == netlist |
||||
|
|
||||
|
def test_savecurrents_with_rser_full_tesla_coil(self): |
||||
|
"""Full Tesla coil netlist: Rser + savecurrents + K coupling + probes.""" |
||||
|
netlist = """Tesla Coil AC Analysis |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
L1 p1 0 10.927u Rser=0.001 |
||||
|
L2 0 top 15.987m Rser=0.001 |
||||
|
C_topload top 0 13.822p |
||||
|
K1 L1 L2 0.3204 |
||||
|
.options savecurrents |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# .options savecurrents removed |
||||
|
assert ".options savecurrents" not in result.lower() |
||||
|
# Rser expanded |
||||
|
assert "R_L1_ser" in result |
||||
|
assert "R_L2_ser" in result |
||||
|
# Capacitor probes inserted |
||||
|
assert "V_probe_C_mmc" in result |
||||
|
assert "V_probe_C_topload" in result |
||||
|
# .save directives present |
||||
|
assert ".save all" in result |
||||
|
assert "i(V1)" in result |
||||
|
assert "i(L1)" in result |
||||
|
assert "i(L2)" in result |
||||
|
assert "i(R_L1_ser)" in result |
||||
|
assert "i(R_L2_ser)" in result |
||||
|
assert "i(V_probe_C_mmc)" in result |
||||
|
assert "i(V_probe_C_topload)" in result |
||||
|
# K element NOT in saves |
||||
|
assert "i(K1)" not in result |
||||
|
# .end still present |
||||
|
assert ".end" in result |
||||
|
|
||||
|
def test_save_directives_before_end(self): |
||||
|
""".save directives should appear before .end.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
R1 in 0 1k |
||||
|
.options savecurrents |
||||
|
.op |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
lines = result.splitlines() |
||||
|
save_idx = next(i for i, l in enumerate(lines) if '.save all' in l) |
||||
|
end_idx = next(i for i, l in enumerate(lines) if l.strip().lower() == '.end') |
||||
|
assert save_idx < end_idx |
||||
|
|
||||
|
|
||||
|
class TestIsSaveCurrentsOption: |
||||
|
"""Test the _is_savecurrents_option helper.""" |
||||
|
|
||||
|
def test_standard(self): |
||||
|
assert _is_savecurrents_option(".options savecurrents") |
||||
|
|
||||
|
def test_uppercase(self): |
||||
|
assert _is_savecurrents_option(".OPTIONS SAVECURRENTS") |
||||
|
|
||||
|
def test_mixed_case(self): |
||||
|
assert _is_savecurrents_option(".Options SaveCurrents") |
||||
|
|
||||
|
def test_singular_option(self): |
||||
|
assert _is_savecurrents_option(".option savecurrents") |
||||
|
|
||||
|
def test_extra_spaces(self): |
||||
|
assert _is_savecurrents_option(".options savecurrents") |
||||
|
|
||||
|
def test_not_savecurrents(self): |
||||
|
assert not _is_savecurrents_option(".options reltol=1e-4") |
||||
|
|
||||
|
def test_not_options(self): |
||||
|
assert not _is_savecurrents_option("V1 in 0 1") |
||||
|
|
||||
|
|
||||
|
class TestCollectComponentNames: |
||||
|
"""Test the _collect_component_names helper.""" |
||||
|
|
||||
|
def test_basic(self): |
||||
|
lines = ["Title", "V1 in 0 1", "R1 in out 1k", ".tran 1u 10u", ".end"] |
||||
|
names = _collect_component_names(lines) |
||||
|
assert names == ["V1", "R1"] |
||||
|
|
||||
|
def test_skips_comments(self): |
||||
|
lines = ["* Comment", "V1 in 0 1", "* Another comment", ".end"] |
||||
|
names = _collect_component_names(lines) |
||||
|
assert names == ["V1"] |
||||
|
|
||||
|
def test_skips_K_elements(self): |
||||
|
lines = ["V1 in 0 1", "L1 in 0 10u", "K1 L1 L2 0.5", ".end"] |
||||
|
names = _collect_component_names(lines) |
||||
|
assert "K1" not in names |
||||
|
assert "V1" in names |
||||
|
assert "L1" in names |
||||
|
|
||||
|
def test_skips_directives(self): |
||||
|
lines = [".tran 1u 10u", "V1 in 0 1", ".end"] |
||||
|
names = _collect_component_names(lines) |
||||
|
assert names == ["V1"] |
||||
|
|
||||
|
|
||||
|
class TestCapacitorProbes: |
||||
|
"""Test capacitor current probe insertion (0V voltage sources).""" |
||||
|
|
||||
|
def test_probe_inserted(self): |
||||
|
"""Basic capacitor should get a 0V probe voltage source.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
.options savecurrents |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'C1 _probe_C1 out 1u' in result |
||||
|
assert 'V_probe_C1 in _probe_C1 0' in result |
||||
|
|
||||
|
def test_probe_multiple_caps(self): |
||||
|
"""Multiple capacitors should each get separate probes.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in mid 1u |
||||
|
C2 mid out 2u |
||||
|
.options savecurrents |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'V_probe_C1' in result |
||||
|
assert 'V_probe_C2' in result |
||||
|
assert '_probe_C1' in result |
||||
|
assert '_probe_C2' in result |
||||
|
|
||||
|
def test_probe_preserves_params(self): |
||||
|
"""Capacitor parameters (IC=, etc.) should be preserved.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
C1 in out 1u IC=0.5 |
||||
|
.options savecurrents |
||||
|
.tran 1u 100u |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'C1 _probe_C1 out 1u IC=0.5' in result |
||||
|
|
||||
|
def test_probe_only_with_savecurrents(self): |
||||
|
"""No probes should be inserted without .options savecurrents.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert '_probe_' not in result |
||||
|
assert 'V_probe_' not in result |
||||
|
|
||||
|
def test_probe_with_rser_combined(self): |
||||
|
"""Full Tesla coil: Rser + probes + savecurrents.""" |
||||
|
netlist = """Tesla Coil AC Analysis |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
L1 p1 0 10.927u Rser=0.001 |
||||
|
L2 0 top 15.987m Rser=0.001 |
||||
|
C_topload top 0 13.822p |
||||
|
K1 L1 L2 0.3204 |
||||
|
.options savecurrents |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Rser expanded |
||||
|
assert 'R_L1_ser' in result |
||||
|
assert 'R_L2_ser' in result |
||||
|
# Capacitor probes inserted |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_topload' in result |
||||
|
assert 'C_mmc _probe_C_mmc p1 0.03u' in result |
||||
|
assert 'C_topload _probe_C_topload 0 13.822p' in result |
||||
|
# Probe V sources in .save |
||||
|
assert 'i(V_probe_C_mmc)' in result |
||||
|
assert 'i(V_probe_C_topload)' in result |
||||
|
# K element NOT in saves |
||||
|
assert 'i(K1)' not in result |
||||
|
|
||||
|
def test_probe_naming_convention(self): |
||||
|
"""Probe names should follow V_probe_Cname convention.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
C_mmc in 0 1u |
||||
|
.options savecurrents |
||||
|
.op |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert '_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_mmc in _probe_C_mmc 0' in result |
||||
|
|
||||
|
def test_probe_not_inside_subckt(self): |
||||
|
"""Capacitors inside .subckt should NOT get probes.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
.subckt mycomp a b |
||||
|
C2 a b 10u |
||||
|
.ends mycomp |
||||
|
.options savecurrents |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'V_probe_C1' in result # top-level probed |
||||
|
assert 'V_probe_C2' not in result # subcircuit NOT probed |
||||
|
|
||||
|
def test_probe_before_end(self): |
||||
|
"""Probe V sources should be inserted before .end.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 1 |
||||
|
C1 in 0 1u |
||||
|
.options savecurrents |
||||
|
.op |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
lines = result.splitlines() |
||||
|
probe_idx = next(i for i, l in enumerate(lines) if 'V_probe_C1' in l) |
||||
|
end_idx = next(i for i, l in enumerate(lines) if l.strip().lower() == '.end') |
||||
|
assert probe_idx < end_idx |
||||
|
|
||||
|
|
||||
|
class TestProcessCapacitorProbe: |
||||
|
"""Test the _process_capacitor_probe helper.""" |
||||
|
|
||||
|
def test_basic(self): |
||||
|
mod, probe = _process_capacitor_probe("C1 in out 1u") |
||||
|
assert mod == "C1 _probe_C1 out 1u" |
||||
|
assert probe == "V_probe_C1 in _probe_C1 0" |
||||
|
|
||||
|
def test_named_cap(self): |
||||
|
mod, probe = _process_capacitor_probe("C_mmc vin p1 0.03u") |
||||
|
assert mod == "C_mmc _probe_C_mmc p1 0.03u" |
||||
|
assert probe == "V_probe_C_mmc vin _probe_C_mmc 0" |
||||
|
|
||||
|
def test_preserves_params(self): |
||||
|
mod, probe = _process_capacitor_probe("C1 a b 10u IC=0.5") |
||||
|
assert "IC=0.5" in mod |
||||
|
assert "_probe_C1" in mod |
||||
|
|
||||
|
def test_too_few_tokens(self): |
||||
|
"""Lines with fewer than 4 tokens should be returned unchanged.""" |
||||
|
mod, probe = _process_capacitor_probe("C1 a") |
||||
|
assert mod == "C1 a" |
||||
|
assert probe == "" |
||||
|
|
||||
|
|
||||
|
class TestParseSaveCapCurrents: |
||||
|
"""Test the _parse_save_cap_currents helper.""" |
||||
|
|
||||
|
def test_single_cap(self): |
||||
|
result = _parse_save_cap_currents(".save i(C_mmc)") |
||||
|
assert result == ["C_mmc"] |
||||
|
|
||||
|
def test_multiple_caps(self): |
||||
|
result = _parse_save_cap_currents(".save i(C_mmc) i(C_topload)") |
||||
|
assert result == ["C_mmc", "C_topload"] |
||||
|
|
||||
|
def test_non_cap_ignored(self): |
||||
|
result = _parse_save_cap_currents(".save i(V1) i(R1) i(L1)") |
||||
|
assert result == [] |
||||
|
|
||||
|
def test_mixed(self): |
||||
|
result = _parse_save_cap_currents(".save i(C_mmc) i(V1) i(C_topload)") |
||||
|
assert result == ["C_mmc", "C_topload"] |
||||
|
|
||||
|
def test_voltage_saves_ignored(self): |
||||
|
result = _parse_save_cap_currents(".save v(out) v(in)") |
||||
|
assert result == [] |
||||
|
|
||||
|
def test_not_save_directive(self): |
||||
|
result = _parse_save_cap_currents("V1 in 0 1") |
||||
|
assert result == [] |
||||
|
|
||||
|
def test_case_insensitive(self): |
||||
|
result = _parse_save_cap_currents(".SAVE I(C_mmc)") |
||||
|
assert result == ["C_mmc"] |
||||
|
|
||||
|
def test_save_all(self): |
||||
|
result = _parse_save_cap_currents(".save all") |
||||
|
assert result == [] |
||||
|
|
||||
|
|
||||
|
class TestTargetedCapacitorProbes: |
||||
|
"""Test targeted capacitor probing via .save i(C_name) directives.""" |
||||
|
|
||||
|
def test_single_targeted_probe(self): |
||||
|
"""Only the named capacitor should get a probe.""" |
||||
|
netlist = """Tesla Coil |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
C_topload p1 0 13.822p |
||||
|
.save i(C_mmc) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# C_mmc should be probed |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'C_mmc _probe_C_mmc p1 0.03u' in result |
||||
|
# C_topload should NOT be probed |
||||
|
assert 'V_probe_C_topload' not in result |
||||
|
assert '_probe_C_topload' not in result |
||||
|
# .save should reference probe, not original cap |
||||
|
assert 'i(V_probe_C_mmc)' in result |
||||
|
# .save all should be present |
||||
|
assert '.save all' in result |
||||
|
|
||||
|
def test_multiple_targeted_probes_same_line(self): |
||||
|
"""Multiple caps on one .save line should all get probes.""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
C_topload p1 0 13.822p |
||||
|
C_other p1 0 10p |
||||
|
.save i(C_mmc) i(C_topload) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_topload' in result |
||||
|
assert 'V_probe_C_other' not in result |
||||
|
assert 'i(V_probe_C_mmc)' in result |
||||
|
assert 'i(V_probe_C_topload)' in result |
||||
|
|
||||
|
def test_multiple_save_lines(self): |
||||
|
"""Caps on separate .save lines should all get probes.""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
C_topload p1 0 13.822p |
||||
|
.save i(C_mmc) |
||||
|
.save i(C_topload) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_topload' in result |
||||
|
|
||||
|
def test_non_capacitor_save_unchanged(self): |
||||
|
""".save i(V1) and .save i(L1) should pass through without probes.""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
L1 vin 0 10u |
||||
|
C1 vin 0 1u |
||||
|
.save i(V1) i(L1) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# No probes inserted (no capacitor current saves) |
||||
|
assert 'V_probe_' not in result |
||||
|
assert '_probe_' not in result |
||||
|
# Original saves preserved |
||||
|
assert 'i(V1)' in result |
||||
|
assert 'i(L1)' in result |
||||
|
|
||||
|
def test_mixed_save_cap_and_non_cap(self): |
||||
|
""".save with both cap and non-cap currents.""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
L1 p1 0 10u |
||||
|
.save i(C_mmc) i(V1) i(L1) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Cap probed |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
# .save rewritten for cap, others preserved |
||||
|
assert 'i(V_probe_C_mmc)' in result |
||||
|
assert 'i(V1)' in result |
||||
|
assert 'i(L1)' in result |
||||
|
|
||||
|
def test_savecurrents_overrides_targeted(self): |
||||
|
"""When both .options savecurrents and .save i(C_name) exist, |
||||
|
savecurrents takes precedence (probes everything).""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
C_topload p1 0 13.822p |
||||
|
.save i(C_mmc) |
||||
|
.options savecurrents |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Both caps probed (savecurrents probes everything) |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_topload' in result |
||||
|
|
||||
|
def test_no_save_no_probes(self): |
||||
|
"""Without .save or .options savecurrents, no probes should be inserted.""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
C1 vin 0 1u |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'V_probe_' not in result |
||||
|
assert '_probe_' not in result |
||||
|
|
||||
|
def test_targeted_probe_not_inside_subckt(self): |
||||
|
"""Capacitors inside .subckt should NOT get probes even if targeted.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
.subckt mycomp a b |
||||
|
C1 a b 10u |
||||
|
.ends mycomp |
||||
|
.save i(C1) |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Top-level C1 probed |
||||
|
assert 'V_probe_C1' in result |
||||
|
# Only one probe V-source line (not the subckt one) |
||||
|
probe_vsource_lines = [l for l in result.splitlines() |
||||
|
if l.strip().startswith('V_probe_C1')] |
||||
|
assert len(probe_vsource_lines) == 1 |
||||
|
|
||||
|
def test_save_voltage_with_cap_current(self): |
||||
|
""".save v(out) alongside .save i(C1) should both work.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
R1 out 0 1k |
||||
|
.save v(out) i(C1) |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
assert 'V_probe_C1' in result |
||||
|
assert 'v(out)' in result |
||||
|
assert 'i(V_probe_C1)' in result |
||||
|
|
||||
|
def test_targeted_probe_case_insensitive(self): |
||||
|
""".save I(c_mmc) should match C_mmc component (case-insensitive).""" |
||||
|
netlist = """Test |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
.save I(c_mmc) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Should probe C_mmc (case-insensitive match) |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
|
||||
|
def test_save_all_not_duplicated(self): |
||||
|
"""If user already has .save all, don't add another.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
.save all |
||||
|
.save i(C1) |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Should have exactly one .save all |
||||
|
save_all_count = sum(1 for line in result.splitlines() |
||||
|
if line.strip().lower().split() == ['.save', 'all']) |
||||
|
assert save_all_count == 1 |
||||
|
|
||||
|
def test_targeted_with_rser_combined(self): |
||||
|
"""Targeted probing combined with inductor Rser expansion.""" |
||||
|
netlist = """Tesla Coil AC Analysis |
||||
|
V1 vin 0 AC 1 |
||||
|
C_mmc vin p1 0.03u |
||||
|
L1 p1 0 10.927u Rser=0.001 |
||||
|
C_topload top 0 13.822p |
||||
|
.save i(C_mmc) |
||||
|
.ac dec 100 1k 3meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
# Rser expanded |
||||
|
assert 'R_L1_ser' in result |
||||
|
# Only C_mmc probed, not C_topload |
||||
|
assert 'V_probe_C_mmc' in result |
||||
|
assert 'V_probe_C_topload' not in result |
||||
|
# .save rewritten |
||||
|
assert 'i(V_probe_C_mmc)' in result |
||||
|
|
||||
|
def test_probes_before_end(self): |
||||
|
"""Targeted probe V-sources should appear before .end.""" |
||||
|
netlist = """Test |
||||
|
V1 in 0 AC 1 |
||||
|
C1 in out 1u |
||||
|
.save i(C1) |
||||
|
.ac dec 10 1k 1meg |
||||
|
.end |
||||
|
""" |
||||
|
result = preprocess_netlist(netlist) |
||||
|
lines = result.splitlines() |
||||
|
end_idx = next(i for i, l in enumerate(lines) if l.strip().lower() == '.end') |
||||
|
probe_idx = next(i for i, l in enumerate(lines) if 'V_probe_C1' in l and l.strip().startswith('V_probe')) |
||||
|
assert probe_idx < end_idx |
||||
@ -0,0 +1,127 @@ |
|||||
|
"""Tests for the SpiceRunner interface and NgspiceRunner.""" |
||||
|
|
||||
|
import os |
||||
|
import pytest |
||||
|
from pyngspice.runner import NgspiceRunner, SubprocessRunner, SimulationError, get_runner |
||||
|
|
||||
|
|
||||
|
class TestNgspiceRunnerDetect: |
||||
|
"""Test NgspiceRunner.detect() and get_executable_path().""" |
||||
|
|
||||
|
def test_detect(self): |
||||
|
"""NgspiceRunner should detect the embedded C++ extension.""" |
||||
|
from pyngspice import _cpp_available |
||||
|
assert NgspiceRunner.detect() == _cpp_available |
||||
|
|
||||
|
def test_executable_path_is_none(self): |
||||
|
"""Embedded runner has no separate executable.""" |
||||
|
assert NgspiceRunner.get_executable_path() is None |
||||
|
|
||||
|
|
||||
|
@pytest.mark.skipif( |
||||
|
not NgspiceRunner.detect(), |
||||
|
reason="C++ extension not built" |
||||
|
) |
||||
|
class TestNgspiceRunnerSimulation: |
||||
|
"""Integration tests for NgspiceRunner (require built C++ extension).""" |
||||
|
|
||||
|
def test_run_simple_rc(self, rc_netlist, tmp_workdir): |
||||
|
"""Run a simple RC circuit and verify output files exist.""" |
||||
|
runner = NgspiceRunner(working_directory=tmp_workdir) |
||||
|
raw_file, log_file = runner.run(rc_netlist) |
||||
|
|
||||
|
assert os.path.isfile(raw_file) |
||||
|
assert raw_file.endswith(".raw") |
||||
|
|
||||
|
def test_run_with_rser_preprocessing(self, inductor_rser_netlist, tmp_workdir): |
||||
|
"""Run a netlist with Rser= inductors (tests pre-processing).""" |
||||
|
runner = NgspiceRunner(working_directory=tmp_workdir) |
||||
|
raw_file, log_file = runner.run(inductor_rser_netlist) |
||||
|
|
||||
|
assert os.path.isfile(raw_file) |
||||
|
|
||||
|
def test_run_returns_absolute_paths(self, rc_netlist, tmp_workdir): |
||||
|
"""Output paths should be absolute.""" |
||||
|
runner = NgspiceRunner(working_directory=tmp_workdir) |
||||
|
raw_file, log_file = runner.run(rc_netlist) |
||||
|
|
||||
|
assert os.path.isabs(raw_file) |
||||
|
assert os.path.isabs(log_file) |
||||
|
|
||||
|
def test_run_with_savecurrents(self, tesla_coil_netlist, tmp_workdir): |
||||
|
"""Run Tesla coil netlist with .options savecurrents — verify cap currents.""" |
||||
|
runner = NgspiceRunner(working_directory=tmp_workdir) |
||||
|
raw_file, log_file = runner.run(tesla_coil_netlist) |
||||
|
|
||||
|
assert os.path.isfile(raw_file) |
||||
|
assert raw_file.endswith(".raw") |
||||
|
|
||||
|
# Verify raw file contains renamed capacitor current traces |
||||
|
from pyngspice import RawRead |
||||
|
raw = RawRead(raw_file) |
||||
|
trace_names = [n.lower() for n in raw.get_trace_names()] |
||||
|
|
||||
|
# Capacitor currents should appear with original names (post-processed) |
||||
|
assert 'i(c_mmc)' in trace_names, f"Missing i(c_mmc) in {trace_names}" |
||||
|
assert 'i(c_topload)' in trace_names, f"Missing i(c_topload) in {trace_names}" |
||||
|
|
||||
|
# Probe names should NOT appear (renamed away) |
||||
|
assert 'i(v_probe_c_mmc)' not in trace_names |
||||
|
assert 'i(v_probe_c_topload)' not in trace_names |
||||
|
|
||||
|
def test_run_nonexistent_netlist(self, tmp_workdir): |
||||
|
"""Should raise FileNotFoundError for missing netlist.""" |
||||
|
runner = NgspiceRunner(working_directory=tmp_workdir) |
||||
|
with pytest.raises(FileNotFoundError): |
||||
|
runner.run("/nonexistent/file.net") |
||||
|
|
||||
|
def test_working_directory_created(self, rc_netlist): |
||||
|
"""Working directory should be created if it doesn't exist.""" |
||||
|
import tempfile |
||||
|
workdir = os.path.join(tempfile.gettempdir(), "pyngspice_test_auto_create") |
||||
|
try: |
||||
|
runner = NgspiceRunner(working_directory=workdir) |
||||
|
assert os.path.isdir(workdir) |
||||
|
finally: |
||||
|
if os.path.isdir(workdir): |
||||
|
os.rmdir(workdir) |
||||
|
|
||||
|
|
||||
|
class TestSubprocessRunner: |
||||
|
"""Tests for SubprocessRunner.""" |
||||
|
|
||||
|
def test_detect(self): |
||||
|
"""detect() should return bool without error.""" |
||||
|
result = SubprocessRunner.detect() |
||||
|
assert isinstance(result, bool) |
||||
|
|
||||
|
def test_get_executable_path(self): |
||||
|
"""get_executable_path() should return str or None.""" |
||||
|
result = SubprocessRunner.get_executable_path() |
||||
|
assert result is None or isinstance(result, str) |
||||
|
|
||||
|
|
||||
|
class TestGetRunner: |
||||
|
"""Tests for the get_runner factory function.""" |
||||
|
|
||||
|
def test_auto_returns_runner(self): |
||||
|
"""Auto mode should return some runner if anything is available.""" |
||||
|
try: |
||||
|
runner = get_runner(backend="auto") |
||||
|
assert isinstance(runner, (NgspiceRunner, SubprocessRunner)) |
||||
|
except RuntimeError: |
||||
|
pytest.skip("No ngspice backend available") |
||||
|
|
||||
|
def test_invalid_backend(self): |
||||
|
"""Invalid backend name should raise ValueError.""" |
||||
|
with pytest.raises(ValueError, match="Unknown backend"): |
||||
|
get_runner(backend="invalid") |
||||
|
|
||||
|
@pytest.mark.skipif( |
||||
|
not NgspiceRunner.detect(), |
||||
|
reason="C++ extension not built" |
||||
|
) |
||||
|
def test_embedded_backend(self, tmp_workdir): |
||||
|
"""Explicitly requesting embedded should return NgspiceRunner.""" |
||||
|
runner = get_runner(tmp_workdir, backend="embedded") |
||||
|
assert isinstance(runner, NgspiceRunner) |
||||
@ -0,0 +1,69 @@ |
|||||
|
"""Basic simulation test for pyngspice (moved from root test_sim.py).""" |
||||
|
|
||||
|
import os |
||||
|
import tempfile |
||||
|
|
||||
|
import pytest |
||||
|
|
||||
|
from pyngspice import _cpp_available |
||||
|
|
||||
|
|
||||
|
@pytest.mark.skipif(not _cpp_available, reason="C++ extension not built") |
||||
|
def test_simulator_basic(): |
||||
|
"""Test the low-level Simulator class with a simple RC circuit.""" |
||||
|
from pyngspice import Simulator |
||||
|
|
||||
|
sim = Simulator() |
||||
|
sim.initialize() |
||||
|
assert sim.is_initialized() |
||||
|
|
||||
|
netlist = """RC Test Circuit |
||||
|
V1 in 0 DC 1 |
||||
|
R1 in out 1k |
||||
|
C1 out 0 1u |
||||
|
.tran 0.1m 10m |
||||
|
.end |
||||
|
""" |
||||
|
|
||||
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.cir', delete=False) as f: |
||||
|
f.write(netlist) |
||||
|
netlist_path = f.name |
||||
|
|
||||
|
try: |
||||
|
sim.load_netlist(netlist_path) |
||||
|
sim.run() |
||||
|
|
||||
|
plots = sim.all_plots() |
||||
|
assert len(plots) > 0 |
||||
|
|
||||
|
vectors = sim.all_vectors(plots[0]) |
||||
|
assert len(vectors) > 0 |
||||
|
finally: |
||||
|
os.unlink(netlist_path) |
||||
|
|
||||
|
|
||||
|
@pytest.mark.skipif(not _cpp_available, reason="C++ extension not built") |
||||
|
def test_simrunner_basic(): |
||||
|
"""Test the SimRunner class with a simple RC circuit.""" |
||||
|
from pyngspice import SimRunner, RawRead |
||||
|
|
||||
|
with tempfile.TemporaryDirectory() as tmpdir: |
||||
|
netlist_path = os.path.join(tmpdir, "rc.cir") |
||||
|
with open(netlist_path, "w") as f: |
||||
|
f.write("""RC Test |
||||
|
V1 in 0 DC 1 |
||||
|
R1 in out 1k |
||||
|
C1 out 0 1u |
||||
|
.tran 0.1m 10m |
||||
|
.end |
||||
|
""") |
||||
|
|
||||
|
runner = SimRunner(output_folder=tmpdir) |
||||
|
raw_file, log_file = runner.run_now(netlist_path) |
||||
|
|
||||
|
assert os.path.isfile(raw_file) |
||||
|
|
||||
|
raw = RawRead(raw_file) |
||||
|
traces = raw.get_trace_names() |
||||
|
assert len(traces) > 0 |
||||
|
assert raw.num_points > 0 |
||||
@ -0,0 +1,220 @@ |
|||||
|
// -*-C++-*- |
||||
|
// FlexLexer.h -- define interfaces for lexical analyzer classes generated |
||||
|
// by flex |
||||
|
|
||||
|
// Copyright (c) 1993 The Regents of the University of California. |
||||
|
// All rights reserved. |
||||
|
// |
||||
|
// This code is derived from software contributed to Berkeley by |
||||
|
// Kent Williams and Tom Epperly. |
||||
|
// |
||||
|
// Redistribution and use in source and binary forms, with or without |
||||
|
// modification, are permitted provided that the following conditions |
||||
|
// are met: |
||||
|
|
||||
|
// 1. Redistributions of source code must retain the above copyright |
||||
|
// notice, this list of conditions and the following disclaimer. |
||||
|
// 2. Redistributions in binary form must reproduce the above copyright |
||||
|
// notice, this list of conditions and the following disclaimer in the |
||||
|
// documentation and/or other materials provided with the distribution. |
||||
|
|
||||
|
// Neither the name of the University nor the names of its contributors |
||||
|
// may be used to endorse or promote products derived from this software |
||||
|
// without specific prior written permission. |
||||
|
|
||||
|
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
||||
|
// IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
|
// PURPOSE. |
||||
|
|
||||
|
// This file defines FlexLexer, an abstract class which specifies the |
||||
|
// external interface provided to flex C++ lexer objects, and yyFlexLexer, |
||||
|
// which defines a particular lexer class. |
||||
|
// |
||||
|
// If you want to create multiple lexer classes, you use the -P flag |
||||
|
// to rename each yyFlexLexer to some other xxFlexLexer. You then |
||||
|
// include <FlexLexer.h> in your other sources once per lexer class: |
||||
|
// |
||||
|
// #undef yyFlexLexer |
||||
|
// #define yyFlexLexer xxFlexLexer |
||||
|
// #include <FlexLexer.h> |
||||
|
// |
||||
|
// #undef yyFlexLexer |
||||
|
// #define yyFlexLexer zzFlexLexer |
||||
|
// #include <FlexLexer.h> |
||||
|
// ... |
||||
|
|
||||
|
#ifndef __FLEX_LEXER_H |
||||
|
// Never included before - need to define base class. |
||||
|
#define __FLEX_LEXER_H |
||||
|
|
||||
|
#include <iostream> |
||||
|
|
||||
|
extern "C++" { |
||||
|
|
||||
|
struct yy_buffer_state; |
||||
|
typedef int yy_state_type; |
||||
|
|
||||
|
class FlexLexer |
||||
|
{ |
||||
|
public: |
||||
|
virtual ~FlexLexer() { } |
||||
|
|
||||
|
const char* YYText() const { return yytext; } |
||||
|
int YYLeng() const { return yyleng; } |
||||
|
|
||||
|
virtual void |
||||
|
yy_switch_to_buffer( yy_buffer_state* new_buffer ) = 0; |
||||
|
virtual yy_buffer_state* yy_create_buffer( std::istream* s, int size ) = 0; |
||||
|
virtual yy_buffer_state* yy_create_buffer( std::istream& s, int size ) = 0; |
||||
|
virtual void yy_delete_buffer( yy_buffer_state* b ) = 0; |
||||
|
virtual void yyrestart( std::istream* s ) = 0; |
||||
|
virtual void yyrestart( std::istream& s ) = 0; |
||||
|
|
||||
|
virtual int yylex() = 0; |
||||
|
|
||||
|
// Call yylex with new input/output sources. |
||||
|
int yylex( std::istream& new_in, std::ostream& new_out ) |
||||
|
{ |
||||
|
switch_streams( new_in, new_out ); |
||||
|
return yylex(); |
||||
|
} |
||||
|
|
||||
|
int yylex( std::istream* new_in, std::ostream* new_out = 0) |
||||
|
{ |
||||
|
switch_streams( new_in, new_out ); |
||||
|
return yylex(); |
||||
|
} |
||||
|
|
||||
|
// Switch to new input/output streams. A nil stream pointer |
||||
|
// indicates "keep the current one". |
||||
|
virtual void switch_streams( std::istream* new_in, |
||||
|
std::ostream* new_out ) = 0; |
||||
|
virtual void switch_streams( std::istream& new_in, |
||||
|
std::ostream& new_out ) = 0; |
||||
|
|
||||
|
int lineno() const { return yylineno; } |
||||
|
|
||||
|
int debug() const { return yy_flex_debug; } |
||||
|
void set_debug( int flag ) { yy_flex_debug = flag; } |
||||
|
|
||||
|
protected: |
||||
|
char* yytext; |
||||
|
int yyleng; |
||||
|
int yylineno; // only maintained if you use %option yylineno |
||||
|
int yy_flex_debug; // only has effect with -d or "%option debug" |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
#endif // FLEXLEXER_H |
||||
|
|
||||
|
#if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce) |
||||
|
// Either this is the first time through (yyFlexLexerOnce not defined), |
||||
|
// or this is a repeated include to define a different flavor of |
||||
|
// yyFlexLexer, as discussed in the flex manual. |
||||
|
# define yyFlexLexerOnce |
||||
|
|
||||
|
extern "C++" { |
||||
|
|
||||
|
class yyFlexLexer : public FlexLexer { |
||||
|
public: |
||||
|
// arg_yyin and arg_yyout default to the cin and cout, but we |
||||
|
// only make that assignment when initializing in yylex(). |
||||
|
yyFlexLexer( std::istream& arg_yyin, std::ostream& arg_yyout ); |
||||
|
yyFlexLexer( std::istream* arg_yyin = 0, std::ostream* arg_yyout = 0 ); |
||||
|
private: |
||||
|
void ctor_common(); |
||||
|
|
||||
|
public: |
||||
|
|
||||
|
virtual ~yyFlexLexer(); |
||||
|
|
||||
|
void yy_switch_to_buffer( yy_buffer_state* new_buffer ); |
||||
|
yy_buffer_state* yy_create_buffer( std::istream* s, int size ); |
||||
|
yy_buffer_state* yy_create_buffer( std::istream& s, int size ); |
||||
|
void yy_delete_buffer( yy_buffer_state* b ); |
||||
|
void yyrestart( std::istream* s ); |
||||
|
void yyrestart( std::istream& s ); |
||||
|
|
||||
|
void yypush_buffer_state( yy_buffer_state* new_buffer ); |
||||
|
void yypop_buffer_state(); |
||||
|
|
||||
|
virtual int yylex(); |
||||
|
virtual void switch_streams( std::istream& new_in, std::ostream& new_out ); |
||||
|
virtual void switch_streams( std::istream* new_in = 0, std::ostream* new_out = 0 ); |
||||
|
virtual int yywrap(); |
||||
|
|
||||
|
protected: |
||||
|
virtual int LexerInput( char* buf, int max_size ); |
||||
|
virtual void LexerOutput( const char* buf, int size ); |
||||
|
virtual void LexerError( const char* msg ); |
||||
|
|
||||
|
void yyunput( int c, char* buf_ptr ); |
||||
|
int yyinput(); |
||||
|
|
||||
|
void yy_load_buffer_state(); |
||||
|
void yy_init_buffer( yy_buffer_state* b, std::istream& s ); |
||||
|
void yy_flush_buffer( yy_buffer_state* b ); |
||||
|
|
||||
|
int yy_start_stack_ptr; |
||||
|
int yy_start_stack_depth; |
||||
|
int* yy_start_stack; |
||||
|
|
||||
|
void yy_push_state( int new_state ); |
||||
|
void yy_pop_state(); |
||||
|
int yy_top_state(); |
||||
|
|
||||
|
yy_state_type yy_get_previous_state(); |
||||
|
yy_state_type yy_try_NUL_trans( yy_state_type current_state ); |
||||
|
int yy_get_next_buffer(); |
||||
|
|
||||
|
std::istream yyin; // input source for default LexerInput |
||||
|
std::ostream yyout; // output sink for default LexerOutput |
||||
|
|
||||
|
// yy_hold_char holds the character lost when yytext is formed. |
||||
|
char yy_hold_char; |
||||
|
|
||||
|
// Number of characters read into yy_ch_buf. |
||||
|
int yy_n_chars; |
||||
|
|
||||
|
// Points to current character in buffer. |
||||
|
char* yy_c_buf_p; |
||||
|
|
||||
|
int yy_init; // whether we need to initialize |
||||
|
int yy_start; // start state number |
||||
|
|
||||
|
// Flag which is used to allow yywrap()'s to do buffer switches |
||||
|
// instead of setting up a fresh yyin. A bit of a hack ... |
||||
|
int yy_did_buffer_switch_on_eof; |
||||
|
|
||||
|
|
||||
|
size_t yy_buffer_stack_top; /**< index of top of stack. */ |
||||
|
size_t yy_buffer_stack_max; /**< capacity of stack. */ |
||||
|
yy_buffer_state ** yy_buffer_stack; /**< Stack as an array. */ |
||||
|
void yyensure_buffer_stack(void); |
||||
|
|
||||
|
// The following are not always needed, but may be depending |
||||
|
// on use of certain flex features (like REJECT or yymore()). |
||||
|
|
||||
|
yy_state_type yy_last_accepting_state; |
||||
|
char* yy_last_accepting_cpos; |
||||
|
|
||||
|
yy_state_type* yy_state_buf; |
||||
|
yy_state_type* yy_state_ptr; |
||||
|
|
||||
|
char* yy_full_match; |
||||
|
int* yy_full_state; |
||||
|
int yy_full_lp; |
||||
|
|
||||
|
int yy_lp; |
||||
|
int yy_looking_for_trail_begin; |
||||
|
|
||||
|
int yy_more_flag; |
||||
|
int yy_more_len; |
||||
|
int yy_more_offset; |
||||
|
int yy_prev_more_offset; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#endif // yyFlexLexer || ! yyFlexLexerOnce |
||||
@ -0,0 +1,34 @@ |
|||||
|
# WinFlexBison - Flex and Bison for Microsoft Windows |
||||
|
|
||||
|
WinFlexBison is a Windows port of [Flex (the fast lexical analyser)](https://github.com/westes/flex/) and [GNU Bison (parser generator)](https://www.gnu.org/software/bison/). |
||||
|
Both win_flex and win_bison are based on upstream sources but depend on system libraries only. |
||||
|
|
||||
|
**NOTE**: |
||||
|
* 2.4.x versions include GNU Bison version 2.7 |
||||
|
* 2.5.x versions include GNU Bison version 3.x.x |
||||
|
|
||||
|
## License |
||||
|
Flex uses a [BSD license](flex/src/COPYING), GNU Bison is [licensed under the GNU General Public License (GPLv3+)](bison/src/COPYING). |
||||
|
All build scripts in WinFlexBison are distributed under GPLv3+. See [COPYING](COPYING) for details. |
||||
|
|
||||
|
All documentation, especially those under custom_build_rules/doc, is distributed under the GNU Free Documentation License (FDL 1.3+). |
||||
|
|
||||
|
## Build status |
||||
|
Bison 3.x (master) [](https://ci.appveyor.com/project/lexxmark/winflexbison/branch/master) and, for compatibility reasons, Bison 2.7 (bison2.7) [](https://ci.appveyor.com/project/lexxmark/winflexbison/branch/bison2.7) |
||||
|
|
||||
|
## Downloads |
||||
|
https://github.com/lexxmark/winflexbison/releases provides stable versions. |
||||
|
To test non-released development versions see the artifacts provided by CI under "Build status". |
||||
|
|
||||
|
## Changelog |
||||
|
The release page includes the full Changelog but you may also see the [changelog.md](changelog.md) file. |
||||
|
|
||||
|
## Build requirements |
||||
|
* Visual Studio 2017 or newer |
||||
|
* optional: CMake (when building with CMake) |
||||
|
|
||||
|
## HowTo |
||||
|
You may use win_flex and win_bison directly on the command line or [use them via CustomBuildRules in VisualStudio](custom_build_rules/README.md). |
||||
|
|
||||
|
## Example flex/bison files |
||||
|
See https://github.com/meyerd/flex-bison-example |
||||
@ -0,0 +1,166 @@ |
|||||
|
## Changelog |
||||
|
|
||||
|
**NOTE**: |
||||
|
* 2.4.x versions include bison version 2.7 |
||||
|
* 2.5.x versions include bison version 3.x |
||||
|
|
||||
|
### version 2.5.25 |
||||
|
* upgrade win_bison to version 3.8.2 |
||||
|
* upgrade m4 to version 1.4.19 |
||||
|
|
||||
|
### version 2.5.24 |
||||
|
* upgrade win_bison to version 3.7.4 |
||||
|
* upgrade m4 to version 1.4.18 |
||||
|
* upgrade gnulib |
||||
|
* removed VS2015 support |
||||
|
* fixed win_bison --update option (renaming opened file) |
||||
|
|
||||
|
### version 2.5.23 |
||||
|
* upgrade win_bison to version 3.7.1 |
||||
|
|
||||
|
### version 2.5.22 |
||||
|
* upgrade win_bison to version 3.5.0 |
||||
|
|
||||
|
### version 2.5.21 |
||||
|
* avoid _m4eof lines in generated bison code while printing warnings |
||||
|
|
||||
|
### version 2.5.20 |
||||
|
* recovered invoking win_bison from different folders |
||||
|
|
||||
|
### version 2.5.19 |
||||
|
* upgrade win_bison to version 3.4.1 |
||||
|
|
||||
|
### version 2.5.18 |
||||
|
* upgrade win_bison to version 3.3.2 |
||||
|
|
||||
|
### version 2.5.17 |
||||
|
* upgrade win_bison to version 3.3.1 |
||||
|
|
||||
|
### version 2.5.16 |
||||
|
* upgrade win_bison to version 3.1 |
||||
|
* write output flex/bison files in binary mode "wb" that means use '\n' EOL not '\r\n' |
||||
|
* documentation about how to use the custom build-rules is now included |
||||
|
|
||||
|
### versions 2.4.12/2.5.15 |
||||
|
* upgrade win_bison to version 3.0.5 |
||||
|
|
||||
|
### versions 2.4.12/2.5.14 |
||||
|
* revert to Visual Studio 2015 due to false positive virus alarms for win_flex.exe |
||||
|
|
||||
|
### versions 2.4.11/2.5.13 |
||||
|
* fixed VS 2017 compilation errors in location.cc |
||||
|
|
||||
|
### versions 2.4.11/2.5.12 |
||||
|
* migrate to Visual Studio 2017 |
||||
|
|
||||
|
### versions 2.4.10/2.5.11 |
||||
|
* upgrade win_flex to version 2.6.4 |
||||
|
* fixed compilation warnings |
||||
|
|
||||
|
### versions 2.4.9/2.5.10 |
||||
|
* data folder was up to dated for bison 3.0.4 |
||||
|
|
||||
|
### versions 2.4.9/2.5.9 |
||||
|
* recovered --header-file win_flex option |
||||
|
|
||||
|
### versions 2.4.8/2.5.8 |
||||
|
* fixed outdated FlexLexer.h file |
||||
|
|
||||
|
### versions 2.4.7/2.5.7 |
||||
|
* upgrade win_flex to version 2.6.3 |
||||
|
* fixed compilation warnings |
||||
|
|
||||
|
### versions 2.4.6/2.5.6 |
||||
|
* upgrade win_bison to version 3.0.4 |
||||
|
* win_bison v2.7 is unchanged |
||||
|
* add separate custom build rules |
||||
|
* for win_bison `custom_build_rules\win_bison_only` |
||||
|
* and win_flex `custom_build_rules\win_flex_only` |
||||
|
|
||||
|
### versions 2.4.5/2.5.5 |
||||
|
* fix missing Additional Options in custom build rules |
||||
|
* fix incorrect "----header-file" option in flex custom build rules |
||||
|
* add some extra flex options to Visual Studio property pages: |
||||
|
1. Prefix (--prefix="...") |
||||
|
2. C++ Class Name (--yyclass="...") |
||||
|
|
||||
|
###versions 2.4.4/2.5.4 |
||||
|
* fix silent errors in custom build rules |
||||
|
* add some flex/bison options to Visual Studio property pages: |
||||
|
* Bison: |
||||
|
1. Output File Name (--output="...") |
||||
|
2. Defines File Name (--defines="...") |
||||
|
3. Debug (--debug) |
||||
|
4. Verbose (--verbose) |
||||
|
5. No lines (--no-lines) |
||||
|
6. File Prefix (--file-prefix="...") |
||||
|
7. Graph File (--graph="...") |
||||
|
8. Warnings (--warnings="...") |
||||
|
9. Report (--report="...") |
||||
|
10. Report File Name (--report-file="...") |
||||
|
|
||||
|
* Flex: |
||||
|
1. Output File Name (--outfile="...") |
||||
|
2. Header File Name (--header-file="...") |
||||
|
3. Windows compatibility mode (--wincompat) |
||||
|
4. Case-insensitive mode (--case-insensitive) |
||||
|
5. Lex-compatibility mode (--lex-compat) |
||||
|
6. Start Condition Stacks (--stack) |
||||
|
7. Bison Bridge Mode (--bison-bridge) |
||||
|
8. No #line Directives (--noline) |
||||
|
9. Generate Reentrant Scanner (--reentrant) |
||||
|
10. Generate C++ Scanner (--c++) |
||||
|
11. Debug Mode (--debug) |
||||
|
|
||||
|
### versions 2.4.3/2.5.3 |
||||
|
* fix incorrect #line directives in win_flex.exe |
||||
|
see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=542482 |
||||
|
|
||||
|
### versions 2.4.2/2.5.2 |
||||
|
* backport parallel invocations of win_bison version 2.7 |
||||
|
* win_bison of version 3.0 is unchanged |
||||
|
|
||||
|
### versions 2.4.1/2.5.1 |
||||
|
* remove XSI extention syntax for fprintf function (not implemented in windows) |
||||
|
* this fixes Graphviz files generation for bison |
||||
|
|
||||
|
**NOTE**: |
||||
|
* 2.4.x versions will include bison version 2.7 |
||||
|
* 2.5.x versions will include bison version 3.0 |
||||
|
|
||||
|
### version 2.5 |
||||
|
* upgrade win_bison to version 3.0 and make temporary win_bison's files process unique (so parallel invocations of win_bison are possible) |
||||
|
|
||||
|
**NOTE**: Several deprecated features were removed in bison 3.0 so this version can break your projects. |
||||
|
Please see http://savannah.gnu.org/forum/forum.php?forum_id=7663 |
||||
|
For the reason of compatibility I don't change win_flex_bison-latest.zip to refer to win_flex_bison-2.5.zip file. |
||||
|
It still refer to win_flex_bison-2.4.zip |
||||
|
|
||||
|
### version 2.4 |
||||
|
* fix problem with "m4_syscmd is not implemented" message. |
||||
|
* Now win_bison should output correct diagnostic and error messages. |
||||
|
|
||||
|
### version 2.3 |
||||
|
* hide __attribute__ construction for non GCC compilers |
||||
|
|
||||
|
### version 2.2 |
||||
|
* added --wincompat option to win_flex (this option changes `<unistd.h>` unix include with `<io.h>` windows analog |
||||
|
also `isatty/fileno` functions changed to `_isatty/_fileno`) |
||||
|
fixed two "'<' : signed/unsigned mismatch" warnings in win_flex generated file |
||||
|
|
||||
|
### version 2.1 |
||||
|
* fixed crash when execute win_bison.exe under WindowsXP (argv[0] don't have full application path) |
||||
|
* added win_flex_bison-latest.zip package to freeze download link |
||||
|
|
||||
|
### version 2.0 |
||||
|
* upgrade win_bison to version 2.7 and win_flex to version 2.5.37 |
||||
|
|
||||
|
### version 1.2 |
||||
|
* fixed win_flex.exe #line directives (some #line directives in output file were with unescaped backslashes) |
||||
|
|
||||
|
### version 1.1 |
||||
|
* fixed win_flex.exe parallel invocations (now all temporary files are process specific) |
||||
|
* added FLEX_TMP_DIR environment variable support to redirect temporary files folder |
||||
|
* added '.exe' to program name in win_flex.exe --version output (CMake support) |
||||
|
* fixed win_bison.exe to use "/data" subfolder related to executable path rather than current working directory |
||||
|
* added BISON_PKGDATADIR environment variable to redirect "/data" subfolder to a different place |
||||
@ -0,0 +1,173 @@ |
|||||
|
|
||||
|
How to setup custom build rules for Visual Studio 2010 and up. |
||||
|
--------------- |
||||
|
|
||||
|
First of all you should have the necessary files. |
||||
|
Custom Build rules are separated into a file triplet of `.xml`, `.targets` and `.props`. |
||||
|
|
||||
|
You find the custom build rules for win_flex_bison in the **custom_build_rules** directory of the win_flex_bison archive. |
||||
|
|
||||
|
You may choose to install one of the following rule set |
||||
|
|
||||
|
* the combined rules - [alternative download][1]: |
||||
|
* [win_flex_bison_custom_build.props ](win_flex_bison/win_flex_bison_custom_build.props) |
||||
|
* [win_flex_bison_custom_build.targets](win_flex_bison/win_flex_bison_custom_build.targets) |
||||
|
* [win_flex_bison_custom_build.xml ](win_flex_bison/win_flex_bison_custom_build.xml) |
||||
|
|
||||
|
* flex only rules - [alternative download][2]: |
||||
|
* [win_flex_custom_build.props ](win_flex_only/win_flex_custom_build.props) |
||||
|
* [win_flex_custom_build.targets ](win_flex_only/win_flex_custom_build.targets) |
||||
|
* [win_flex_custom_build.xml ](win_flex_only/win_flex_custom_build.xml) |
||||
|
|
||||
|
* bison only rules - [alternative download][3]: |
||||
|
* [win_bison_custom_build.props ](win_bison_only/win_bison_custom_build.props) |
||||
|
* [win_bison_custom_build.targets](win_bison_only/win_bison_custom_build.targets) |
||||
|
* [win_bison_custom_build.xml ](win_bison_only/win_bison_custom_build.xml) |
||||
|
|
||||
|
This documentation uses the combined rule-set but can be used for all rule-sets. |
||||
|
|
||||
|
[1]: https://sourceforge.net/projects/winflexbison/files/win_flex_bison_custom_build_rules.zip/download "Combined build rules for Bison and Flex" |
||||
|
[2]: https://sourceforge.net/projects/winflexbison/files/win_flex_custom_build_rules.zip/download "Build rules for Flex only" |
||||
|
[3]: https://sourceforge.net/projects/winflexbison/files/win_bison_custom_build_rules.zip/download "Build rules for Bison only" |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
Launch Visual Studio and open an VC/VC++ project. |
||||
|
Open context menu for project item in Solution Explorer panel and select "**Build Customizations...**" menu item. |
||||
|
(Note: newer VS versions have this below sub-menu "**Build Dependencies...**".) |
||||
|
|
||||
|
 |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
In popup dialog "Visual C++ Build Customization Files" press "**Find Existing...**" button. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
In Open File dialog select "**win_flex_bison_custom_build.targets**" file and press "Open". |
||||
|
(Note: you may have to switch the file filter to "(*.*) all files".) |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
You will see "Add Search Path?" message box, press "Yes". |
||||
|
|
||||
|
 |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
In "Visual C++ Build Customization Files" dialog check just added item **win_flex_bison_custom_build** and press "OK". |
||||
|
|
||||
|
 |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
Now you can add flex and bison files to the project... |
||||
|
|
||||
|
 |
||||
|
|
||||
|
... and build. |
||||
|
In build output you should see something like this: |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
1>------ Rebuild All started: Project: ConsoleApplication1, Configuration: Debug Win32 ------ |
||||
|
1> Process sample bison file |
||||
|
1> Process sample flex file |
||||
|
1> stdafx.cpp |
||||
|
1> ConsoleApplication1.cpp |
||||
|
1> Generating Code... |
||||
|
1> ConsoleApplication1.vcxproj -> C:\Users\ConsoleApplication1\Debug\ConsoleApplication1.exe |
||||
|
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ========== |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
----- |
||||
|
|
||||
|
For **sample.y** bison file there are two output files: **sample.tab.h** and **sample.tab.cpp**. |
||||
|
For **sample.l** flex file you'll got **sample.flex.cpp**. |
||||
|
|
||||
|
Now you can add them to the project and build. (*Don't forget to exclude cpp files from using precompiled headers*) |
||||
|
|
||||
|
 |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------ |
||||
|
1> Process sample bison file |
||||
|
1> Process sample flex file |
||||
|
1> sample.tab.cpp |
||||
|
1> sample.flex.cpp |
||||
|
1> Generating Code... |
||||
|
1> ConsoleApplication1.vcxproj -> C:\Users\ConsoleApplication1\Debug\ConsoleApplication1.exe |
||||
|
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ========== |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
If your flex/bison file is incorrect and you've got an error. But you don't see actual error message, something like this: |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
1>------ Build started: Project: ConsoleApplication2, Configuration: Debug Win32 ------ |
||||
|
1> Process "grammar.y" bison file |
||||
|
1>C:...\custom_build_rules\win_flex_bison_custom_build.targets(55,5): error MSB3721: The command " |
||||
|
1>C:...\custom_build_rules\win_flex_bison_custom_build.targets(55,5): error MSB3721: start /B /WAIT /D "C:...\ConsoleApplication2\ConsoleApplication2\" win_bison.exe --output="grammar.tab.cpp" --defines="grammar.tab.h" --graph="1.dot" "grammar.y" |
||||
|
1>C:...\custom_build_rules\win_flex_bison_custom_build.targets(55,5): error MSB3721: exit /b %errorlevel%" exited with code 1. |
||||
|
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
You can change Build Output Verbosity from "Minimal" to "Normal" in "Options" dialog |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Then you will see more detailed output: |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
1>BisonTarget: |
||||
|
1> Process "grammar.y" bison file |
||||
|
1> grammar.y:51.1-4: error: invalid directive: '%sdw' |
||||
|
1>C:...\custom_build_rules\win_flex_bison_custom_build.targets(55,5): error MSB3721: The command " |
||||
|
1>C:...\custom_build_rules\win_flex_bison_custom_build.targets(55,5): error MSB3721: start /B /WAIT /D "C:...\ConsoleApplication2\" win_bison.exe --output="grammar.tab.cpp" --defines="grammar.tab.h" --graph="1.dot" "grammar.y" |
||||
|
1>C:...\custom_build_rules\win_flex_bison_custom_build.targets(55,5): error MSB3721: exit /b %errorlevel%" exited with code 1. |
||||
|
1> |
||||
|
1>Build FAILED. |
||||
|
1> |
||||
|
1>Time Elapsed 00:00:01.21 |
||||
|
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== |
||||
|
|
||||
|
~~~~ |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
Also you can tune some flex/bison options in files properties dialog: |
||||
|
|
||||
|
 |
||||
|
 |
||||
|
 |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
|
||||
|
To debug your scanner or parser you can set break points right into **sample.y** or **sample.l** code. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
|
||||
|
To use the Visual C++ Code editor for Flex/Bison files instead of the text editor adjust your editor settings as follows: |
||||
|
|
||||
|
* click **Options** on the **Tools** menu, expand the **Text Editor** node and select **File Extension** |
||||
|
* type extension **`l`** in the **Extension** field and choose **Microsoft Visual C++** in the **Editor** drop-down field, click **Add** |
||||
|
* do the same for the extension **`y`** |
||||
|
|
||||
|
You now have syntax highlighting, code-completion and show definition options in your Flex/Bison source. |
||||
|
|
||||
|
---- |
||||
|
|
||||
|
Enjoy! |
||||
|
After Width: 531 | Height: 603 | Size: 31 KiB |
|
After Width: 602 | Height: 359 | Size: 11 KiB |
|
After Width: 496 | Height: 160 | Size: 7.1 KiB |
|
After Width: 602 | Height: 359 | Size: 12 KiB |
|
After Width: 345 | Height: 250 | Size: 11 KiB |
|
After Width: 346 | Height: 364 | Size: 15 KiB |
|
After Width: 774 | Height: 452 | Size: 26 KiB |
|
After Width: 776 | Height: 453 | Size: 26 KiB |
|
After Width: 985 | Height: 429 | Size: 27 KiB |
|
After Width: 409 | Height: 603 | Size: 31 KiB |
|
After Width: 776 | Height: 458 | Size: 29 KiB |
@ -0,0 +1,23 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup |
||||
|
Condition="'$(BisonBeforeTargets)' == '' and '$(BisonAfterTargets)' == '' and '$(ConfigurationType)' != 'Makefile'"> |
||||
|
<BisonBeforeTargets>Midl</BisonBeforeTargets> |
||||
|
<BisonAfterTargets>CustomBuild</BisonAfterTargets> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup> |
||||
|
<BisonDependsOn |
||||
|
Condition="'$(ConfigurationType)' != 'Makefile'">_SelectedFiles;$(BisonDependsOn)</BisonDependsOn> |
||||
|
</PropertyGroup> |
||||
|
<ItemDefinitionGroup> |
||||
|
<Bison> |
||||
|
<OutputFile>%(Filename).tab.cpp</OutputFile> |
||||
|
<DefinesFile>%(Filename).tab.h</DefinesFile> |
||||
|
<CommandLineTemplate> |
||||
|
start /B /WAIT /D "%(RootDir)%(Directory)" win_bison.exe [AllOptions] [AdditionalOptions] "%(Filename)%(Extension)" |
||||
|
exit /b %errorlevel%</CommandLineTemplate> |
||||
|
<Outputs>%(RootDir)%(Directory)%(OutputFile);%(RootDir)%(Directory)%(DefinesFile);</Outputs> |
||||
|
<ExecutionDescription>Process "%(Filename)%(Extension)" bison file</ExecutionDescription> |
||||
|
</Bison> |
||||
|
</ItemDefinitionGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,91 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<PropertyPageSchema |
||||
|
Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" /> |
||||
|
<AvailableItemName |
||||
|
Include="Bison"> |
||||
|
<Targets>BisonTarget</Targets> |
||||
|
</AvailableItemName> |
||||
|
</ItemGroup> |
||||
|
<UsingTask |
||||
|
TaskName="Bison" |
||||
|
TaskFactory="XamlTaskFactory" |
||||
|
AssemblyName="Microsoft.Build.Tasks.v4.0"> |
||||
|
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task> |
||||
|
</UsingTask> |
||||
|
<Target |
||||
|
Name="BisonTarget" |
||||
|
BeforeTargets="$(BisonBeforeTargets)" |
||||
|
AfterTargets="$(BisonAfterTargets)" |
||||
|
Condition="'@(Bison)' != ''" |
||||
|
DependsOnTargets="$(BisonDependsOn);ComputeBisonOutput" |
||||
|
Outputs="%(Bison.Outputs)" |
||||
|
Inputs="%(Bison.Identity);%(Bison.AdditionalDependencies);$(MSBuildProjectFile)"> |
||||
|
<ItemGroup |
||||
|
Condition="'@(SelectedFiles)' != ''"> |
||||
|
<Bison |
||||
|
Remove="@(Bison)" |
||||
|
Condition="'%(Identity)' != '@(SelectedFiles)'" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Bison_tlog |
||||
|
Include="%(Bison.Outputs)" |
||||
|
Condition="'%(Bison.Outputs)' != '' and '%(Bison.ExcludedFromBuild)' != 'true'"> |
||||
|
<Source>@(Bison, '|')</Source> |
||||
|
</Bison_tlog> |
||||
|
</ItemGroup> |
||||
|
<Message |
||||
|
Importance="High" |
||||
|
Text="%(Bison.ExecutionDescription)" /> |
||||
|
<WriteLinesToFile |
||||
|
Condition="'@(Bison_tlog)' != '' and '%(Bison_tlog.ExcludedFromBuild)' != 'true'" |
||||
|
File="$(IntDir)$(ProjectName).write.1.tlog" |
||||
|
Lines="^%(Bison_tlog.Source);@(Bison_tlog->'%(Fullpath)')" /> |
||||
|
<Bison |
||||
|
Condition="'@(Bison)' != '' and '%(Bison.ExcludedFromBuild)' != 'true'" |
||||
|
CommandLineTemplate="%(Bison.CommandLineTemplate)" |
||||
|
OutputFile="%(Bison.OutputFile)" |
||||
|
DefinesFile="%(Bison.DefinesFile)" |
||||
|
Debug="%(Bison.Debug)" |
||||
|
Verbose="%(Bison.Verbose)" |
||||
|
NoLines="%(Bison.NoLines)" |
||||
|
FilePrefix="%(Bison.FilePrefix)" |
||||
|
GraphFile="%(Bison.GraphFile)" |
||||
|
Warnings="%(Bison.Warnings)" |
||||
|
Report="%(Bison.Report)" |
||||
|
ReportFile="%(Bison.ReportFile)" |
||||
|
AdditionalOptions="%(Bison.AdditionalOptions)" |
||||
|
Inputs="%(Bison.Identity)" /> |
||||
|
</Target> |
||||
|
<PropertyGroup> |
||||
|
<ComputeLinkInputsTargets> |
||||
|
$(ComputeLinkInputsTargets); |
||||
|
ComputeBisonOutput; |
||||
|
</ComputeLinkInputsTargets> |
||||
|
<ComputeLibInputsTargets> |
||||
|
$(ComputeLibInputsTargets); |
||||
|
ComputeBisonOutput; |
||||
|
</ComputeLibInputsTargets> |
||||
|
</PropertyGroup> |
||||
|
<Target |
||||
|
Name="ComputeBisonOutput" |
||||
|
Condition="'@(Bison)' != ''"> |
||||
|
<ItemGroup> |
||||
|
<BisonDirsToMake |
||||
|
Condition="'@(Bison)' != '' and '%(Bison.ExcludedFromBuild)' != 'true'" |
||||
|
Include="%(Bison.Outputs)" /> |
||||
|
<Link |
||||
|
Include="%(BisonDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<Lib |
||||
|
Include="%(BisonDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<ImpLib |
||||
|
Include="%(BisonDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
</ItemGroup> |
||||
|
<MakeDir |
||||
|
Directories="@(BisonDirsToMake->'%(RootDir)%(Directory)')" /> |
||||
|
</Target> |
||||
|
</Project> |
||||
@ -0,0 +1,281 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:transformCallback="Microsoft.Cpp.Dev10.ConvertPropertyCallback"> |
||||
|
<Rule |
||||
|
Name="Bison" |
||||
|
PageTemplate="tool" |
||||
|
DisplayName="Bison files" |
||||
|
SwitchPrefix="--" |
||||
|
Order="200"> |
||||
|
<Rule.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Bison" /> |
||||
|
</Rule.DataSource> |
||||
|
<Rule.Categories> |
||||
|
<Category |
||||
|
Name="General"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>General</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Bison Options"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Bison Options</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Command Line" |
||||
|
Subtype="CommandLine"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Command Line</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
</Rule.Categories> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="OutputFile" |
||||
|
Category="Bison Options" |
||||
|
IsRequired="true" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Output File Name" |
||||
|
Description="Specify the file for the parser implementation file. --output=value" |
||||
|
Switch="output="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="DefinesFile" |
||||
|
Category="Bison Options" |
||||
|
Subtype="file" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Defines File Name" |
||||
|
Description="Pretend that %defines was specified, i.e., write an extra output file containing macro definitions for the token type names defined in the grammar, as well as a few other declarations. --defines=value" |
||||
|
Switch="defines="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Debug" |
||||
|
Category="Bison Options" |
||||
|
DisplayName="Debug" |
||||
|
Description="In the parser implementation file, define the macro YYDEBUG to 1 if it is not already defined, so that the debugging facilities are compiled. (--debug)" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Enabling-Traces.html#Enabling-Traces" |
||||
|
Switch="debug" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Verbose" |
||||
|
Category="Bison Options" |
||||
|
DisplayName="Verbose" |
||||
|
Description="Write an extra output file containing verbose descriptions of the parser states and what is done for each type of lookahead token in that state. (--verbose)" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Understanding.html#Understanding" |
||||
|
Switch="verbose" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="NoLines" |
||||
|
Category="Bison Options" |
||||
|
DisplayName="No lines" |
||||
|
Description="Don’t put any #line preprocessor commands in the parser implementation file. (--no-lines)" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
Switch="no-lines" /> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="FilePrefix" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="File Prefix" |
||||
|
Description="Pretend that %file-prefix was specified, i.e., specify prefix to use for all Bison output file names. --file-prefix=prefix" |
||||
|
Switch="file-prefix="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="GraphFile" |
||||
|
Category="Bison Options" |
||||
|
Subtype="file" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Graph File" |
||||
|
Description="Output a graphical representation of the parser’s automaton computed by Bison, in Graphviz DOT format. --graph=file" |
||||
|
Switch="graph="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<EnumProperty |
||||
|
Name="Warnings" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Warnings" |
||||
|
Description="Output warnings falling in category. (--warnings=category)"> |
||||
|
|
||||
|
<EnumValue |
||||
|
Name="midrule-values" |
||||
|
DisplayName="midrule-values" |
||||
|
Switch="warnings=midrule-values"/> |
||||
|
<EnumValue |
||||
|
Name="yacc" |
||||
|
DisplayName="yacc" |
||||
|
Switch="warnings=yacc"/> |
||||
|
<EnumValue |
||||
|
Name="conflicts-sr" |
||||
|
DisplayName="conflicts-sr" |
||||
|
Switch="warnings=conflicts-sr"/> |
||||
|
<EnumValue |
||||
|
Name="conflicts-rr" |
||||
|
DisplayName="conflicts-rr" |
||||
|
Switch="warnings=conflicts-rr"/> |
||||
|
<EnumValue |
||||
|
Name="other" |
||||
|
DisplayName="other" |
||||
|
Switch="warnings=other"/> |
||||
|
<EnumValue |
||||
|
Name="all" |
||||
|
DisplayName="all" |
||||
|
Switch="warnings=all"/> |
||||
|
<EnumValue |
||||
|
Name="none" |
||||
|
DisplayName="none" |
||||
|
Switch="warnings=none"/> |
||||
|
<EnumValue |
||||
|
Name="error" |
||||
|
DisplayName="error" |
||||
|
Switch="warnings=error"/> |
||||
|
</EnumProperty> |
||||
|
|
||||
|
<EnumProperty |
||||
|
Name="Report" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Report" |
||||
|
Description="Write an extra output file containing verbose description of the comma separated list of things. (--report=things)"> |
||||
|
|
||||
|
<EnumValue |
||||
|
Name="state" |
||||
|
DisplayName="state" |
||||
|
Switch="report=state"/> |
||||
|
<EnumValue |
||||
|
Name="itemset" |
||||
|
DisplayName="itemset" |
||||
|
Switch="report=itemset"/> |
||||
|
<EnumValue |
||||
|
Name="lookahead" |
||||
|
DisplayName="lookahead" |
||||
|
Switch="report=lookahead"/> |
||||
|
<EnumValue |
||||
|
Name="solved" |
||||
|
DisplayName="solved" |
||||
|
Switch="report=solved"/> |
||||
|
<EnumValue |
||||
|
Name="all" |
||||
|
DisplayName="all" |
||||
|
Switch="report=all"/> |
||||
|
<EnumValue |
||||
|
Name="none" |
||||
|
DisplayName="none" |
||||
|
Switch="report=none"/> |
||||
|
</EnumProperty> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="ReportFile" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Report File Name" |
||||
|
Description="Specify the file for the verbose description. --report-file=value" |
||||
|
Switch="report-file="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="Inputs" |
||||
|
Category="Command Line" |
||||
|
IsRequired="true" |
||||
|
Switch=" "> |
||||
|
<StringListProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Bison" |
||||
|
SourceType="Item" /> |
||||
|
</StringListProperty.DataSource> |
||||
|
</StringListProperty> |
||||
|
<StringProperty |
||||
|
Name="CommandLineTemplate" |
||||
|
DisplayName="Command Line" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<DynamicEnumProperty |
||||
|
Name="BisonBeforeTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute Before</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run before.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^BisonBeforeTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<DynamicEnumProperty |
||||
|
Name="BisonAfterTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute After</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run after.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^BisonAfterTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<StringListProperty |
||||
|
Name="Outputs" |
||||
|
DisplayName="Outputs" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringProperty |
||||
|
Name="ExecutionDescription" |
||||
|
DisplayName="Execution Description" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringListProperty |
||||
|
Name="AdditionalDependencies" |
||||
|
DisplayName="Additional Dependencies" |
||||
|
IncludeInCommandLine="False" |
||||
|
Visible="false" /> |
||||
|
<StringProperty |
||||
|
Subtype="AdditionalOptions" |
||||
|
Name="AdditionalOptions" |
||||
|
Category="Command Line"> |
||||
|
<StringProperty.DisplayName> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.DisplayName> |
||||
|
<StringProperty.Description> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.Description> |
||||
|
</StringProperty> |
||||
|
</Rule> |
||||
|
<ItemType |
||||
|
Name="Bison" |
||||
|
DisplayName="Bison files" /> |
||||
|
<FileExtension |
||||
|
Name="*.y" |
||||
|
ContentType="Bison" /> |
||||
|
<ContentType |
||||
|
Name="Bison" |
||||
|
DisplayName="Bison files" |
||||
|
ItemType="Bison" /> |
||||
|
</ProjectSchemaDefinitions> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup |
||||
|
Condition="'$(BisonBeforeTargets)' == '' and '$(BisonAfterTargets)' == '' and '$(ConfigurationType)' != 'Makefile'"> |
||||
|
<BisonBeforeTargets>Midl</BisonBeforeTargets> |
||||
|
<BisonAfterTargets>CustomBuild</BisonAfterTargets> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup> |
||||
|
<BisonDependsOn |
||||
|
Condition="'$(ConfigurationType)' != 'Makefile'">_SelectedFiles;$(BisonDependsOn)</BisonDependsOn> |
||||
|
</PropertyGroup> |
||||
|
<ItemDefinitionGroup> |
||||
|
<Bison> |
||||
|
<OutputFile>%(Filename).tab.cpp</OutputFile> |
||||
|
<DefinesFile>%(Filename).tab.h</DefinesFile> |
||||
|
<CommandLineTemplate> |
||||
|
start /B /WAIT /D "%(RootDir)%(Directory)" win_bison.exe [AllOptions] [AdditionalOptions] "%(Filename)%(Extension)" |
||||
|
exit /b %errorlevel%</CommandLineTemplate> |
||||
|
<Outputs>%(RootDir)%(Directory)%(OutputFile);%(RootDir)%(Directory)%(DefinesFile);</Outputs> |
||||
|
<ExecutionDescription>Process "%(Filename)%(Extension)" bison file</ExecutionDescription> |
||||
|
</Bison> |
||||
|
</ItemDefinitionGroup> |
||||
|
<PropertyGroup |
||||
|
Condition="'$(FlexBeforeTargets)' == '' and '$(FlexAfterTargets)' == '' and '$(ConfigurationType)' != 'Makefile'"> |
||||
|
<FlexBeforeTargets>Midl</FlexBeforeTargets> |
||||
|
<FlexAfterTargets>CustomBuild</FlexAfterTargets> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup> |
||||
|
<FlexDependsOn |
||||
|
Condition="'$(ConfigurationType)' != 'Makefile'">_SelectedFiles;$(FlexDependsOn)</FlexDependsOn> |
||||
|
</PropertyGroup> |
||||
|
<ItemDefinitionGroup> |
||||
|
<Flex> |
||||
|
<OutputFile>%(Filename).flex.cpp</OutputFile> |
||||
|
<Wincompat>true</Wincompat> |
||||
|
<CommandLineTemplate> |
||||
|
start /B /WAIT /D "%(RootDir)%(Directory)" win_flex.exe [AllOptions] [AdditionalOptions] "%(Filename)%(Extension)" |
||||
|
exit /b %errorlevel%</CommandLineTemplate> |
||||
|
<Outputs>%(RootDir)%(Directory)%(OutputFile);</Outputs> |
||||
|
<ExecutionDescription>Process "%(Filename)%(Extension)" flex file</ExecutionDescription> |
||||
|
</Flex> |
||||
|
</ItemDefinitionGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,178 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<PropertyPageSchema |
||||
|
Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" /> |
||||
|
<AvailableItemName |
||||
|
Include="Bison"> |
||||
|
<Targets>BisonTarget</Targets> |
||||
|
</AvailableItemName> |
||||
|
<AvailableItemName |
||||
|
Include="Flex"> |
||||
|
<Targets>FlexTarget</Targets> |
||||
|
</AvailableItemName> |
||||
|
</ItemGroup> |
||||
|
<UsingTask |
||||
|
TaskName="Bison" |
||||
|
TaskFactory="XamlTaskFactory" |
||||
|
AssemblyName="Microsoft.Build.Tasks.v4.0"> |
||||
|
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task> |
||||
|
</UsingTask> |
||||
|
<UsingTask |
||||
|
TaskName="Flex" |
||||
|
TaskFactory="XamlTaskFactory" |
||||
|
AssemblyName="Microsoft.Build.Tasks.v4.0"> |
||||
|
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task> |
||||
|
</UsingTask> |
||||
|
<Target |
||||
|
Name="BisonTarget" |
||||
|
BeforeTargets="$(BisonBeforeTargets)" |
||||
|
AfterTargets="$(BisonAfterTargets)" |
||||
|
Condition="'@(Bison)' != ''" |
||||
|
DependsOnTargets="$(BisonDependsOn);ComputeBisonOutput" |
||||
|
Outputs="%(Bison.Outputs)" |
||||
|
Inputs="%(Bison.Identity);%(Bison.AdditionalDependencies);$(MSBuildProjectFile)"> |
||||
|
<ItemGroup |
||||
|
Condition="'@(SelectedFiles)' != ''"> |
||||
|
<Bison |
||||
|
Remove="@(Bison)" |
||||
|
Condition="'%(Identity)' != '@(SelectedFiles)'" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Bison_tlog |
||||
|
Include="%(Bison.Outputs)" |
||||
|
Condition="'%(Bison.Outputs)' != '' and '%(Bison.ExcludedFromBuild)' != 'true'"> |
||||
|
<Source>@(Bison, '|')</Source> |
||||
|
</Bison_tlog> |
||||
|
</ItemGroup> |
||||
|
<Message |
||||
|
Importance="High" |
||||
|
Text="%(Bison.ExecutionDescription)" /> |
||||
|
<WriteLinesToFile |
||||
|
Condition="'@(Bison_tlog)' != '' and '%(Bison_tlog.ExcludedFromBuild)' != 'true'" |
||||
|
File="$(IntDir)$(ProjectName).write.1.tlog" |
||||
|
Lines="^%(Bison_tlog.Source);@(Bison_tlog->'%(Fullpath)')" /> |
||||
|
<Bison |
||||
|
Condition="'@(Bison)' != '' and '%(Bison.ExcludedFromBuild)' != 'true'" |
||||
|
CommandLineTemplate="%(Bison.CommandLineTemplate)" |
||||
|
OutputFile="%(Bison.OutputFile)" |
||||
|
DefinesFile="%(Bison.DefinesFile)" |
||||
|
Debug="%(Bison.Debug)" |
||||
|
Verbose="%(Bison.Verbose)" |
||||
|
NoLines="%(Bison.NoLines)" |
||||
|
FilePrefix="%(Bison.FilePrefix)" |
||||
|
GraphFile="%(Bison.GraphFile)" |
||||
|
Warnings="%(Bison.Warnings)" |
||||
|
Report="%(Bison.Report)" |
||||
|
ReportFile="%(Bison.ReportFile)" |
||||
|
AdditionalOptions="%(Bison.AdditionalOptions)" |
||||
|
Inputs="%(Bison.Identity)" /> |
||||
|
</Target> |
||||
|
<PropertyGroup> |
||||
|
<ComputeLinkInputsTargets> |
||||
|
$(ComputeLinkInputsTargets); |
||||
|
ComputeBisonOutput; |
||||
|
</ComputeLinkInputsTargets> |
||||
|
<ComputeLibInputsTargets> |
||||
|
$(ComputeLibInputsTargets); |
||||
|
ComputeBisonOutput; |
||||
|
</ComputeLibInputsTargets> |
||||
|
</PropertyGroup> |
||||
|
<Target |
||||
|
Name="ComputeBisonOutput" |
||||
|
Condition="'@(Bison)' != ''"> |
||||
|
<ItemGroup> |
||||
|
<BisonDirsToMake |
||||
|
Condition="'@(Bison)' != '' and '%(Bison.ExcludedFromBuild)' != 'true'" |
||||
|
Include="%(Bison.Outputs)" /> |
||||
|
<Link |
||||
|
Include="%(BisonDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<Lib |
||||
|
Include="%(BisonDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<ImpLib |
||||
|
Include="%(BisonDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
</ItemGroup> |
||||
|
<MakeDir |
||||
|
Directories="@(BisonDirsToMake->'%(RootDir)%(Directory)')" /> |
||||
|
</Target> |
||||
|
<Target |
||||
|
Name="FlexTarget" |
||||
|
BeforeTargets="$(FlexBeforeTargets)" |
||||
|
AfterTargets="$(FlexAfterTargets)" |
||||
|
Condition="'@(Flex)' != ''" |
||||
|
DependsOnTargets="$(FlexDependsOn);ComputeFlexOutput" |
||||
|
Outputs="%(Flex.Outputs)" |
||||
|
Inputs="%(Flex.Identity);%(Flex.AdditionalDependencies);$(MSBuildProjectFile)"> |
||||
|
<ItemGroup |
||||
|
Condition="'@(SelectedFiles)' != ''"> |
||||
|
<Flex |
||||
|
Remove="@(Flex)" |
||||
|
Condition="'%(Identity)' != '@(SelectedFiles)'" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Flex_tlog |
||||
|
Include="%(Flex.Outputs)" |
||||
|
Condition="'%(Flex.Outputs)' != '' and '%(Flex.ExcludedFromBuild)' != 'true'"> |
||||
|
<Source>@(Flex, '|')</Source> |
||||
|
</Flex_tlog> |
||||
|
</ItemGroup> |
||||
|
<Message |
||||
|
Importance="High" |
||||
|
Text="%(Flex.ExecutionDescription)" /> |
||||
|
<WriteLinesToFile |
||||
|
Condition="'@(Flex_tlog)' != '' and '%(Flex_tlog.ExcludedFromBuild)' != 'true'" |
||||
|
File="$(IntDir)$(ProjectName).write.1.tlog" |
||||
|
Lines="^%(Flex_tlog.Source);@(Flex_tlog->'%(Fullpath)')" /> |
||||
|
<Flex |
||||
|
Condition="'@(Flex)' != '' and '%(Flex.ExcludedFromBuild)' != 'true'" |
||||
|
CommandLineTemplate="%(Flex.CommandLineTemplate)" |
||||
|
OutputFile="%(Flex.OutputFile)" |
||||
|
HeaderFile="%(Flex.HeaderFile)" |
||||
|
Prefix="%(Flex.Prefix)" |
||||
|
Wincompat="%(Flex.Wincompat)" |
||||
|
CaseInsensitive="%(Flex.CaseInsensitive)" |
||||
|
LexCompat="%(Flex.LexCompat)" |
||||
|
Stack="%(Flex.Stack)" |
||||
|
BisonBridge="%(Flex.BisonBridge)" |
||||
|
Noline="%(Flex.Noline)" |
||||
|
Reentrant="%(Flex.Reentrant)" |
||||
|
Cpp="%(Flex.Cpp)" |
||||
|
CppClassName="%(Flex.CppClassName)" |
||||
|
Debug="%(Flex.Debug)" |
||||
|
AdditionalOptions="%(Flex.AdditionalOptions)" |
||||
|
Inputs="%(Flex.Identity)" /> |
||||
|
</Target> |
||||
|
<PropertyGroup> |
||||
|
<ComputeLinkInputsTargets> |
||||
|
$(ComputeLinkInputsTargets); |
||||
|
ComputeFlexOutput; |
||||
|
</ComputeLinkInputsTargets> |
||||
|
<ComputeLibInputsTargets> |
||||
|
$(ComputeLibInputsTargets); |
||||
|
ComputeFlexOutput; |
||||
|
</ComputeLibInputsTargets> |
||||
|
</PropertyGroup> |
||||
|
<Target |
||||
|
Name="ComputeFlexOutput" |
||||
|
Condition="'@(Flex)' != ''"> |
||||
|
<ItemGroup> |
||||
|
<FlexDirsToMake |
||||
|
Condition="'@(Flex)' != '' and '%(Flex.ExcludedFromBuild)' != 'true'" |
||||
|
Include="%(Flex.Outputs)" /> |
||||
|
<Link |
||||
|
Include="%(FlexDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<Lib |
||||
|
Include="%(FlexDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<ImpLib |
||||
|
Include="%(FlexDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
</ItemGroup> |
||||
|
<MakeDir |
||||
|
Directories="@(FlexDirsToMake->'%(RootDir)%(Directory)')" /> |
||||
|
</Target> |
||||
|
</Project> |
||||
@ -0,0 +1,521 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:transformCallback="Microsoft.Cpp.Dev10.ConvertPropertyCallback"> |
||||
|
<Rule |
||||
|
Name="Bison" |
||||
|
PageTemplate="tool" |
||||
|
DisplayName="Bison files" |
||||
|
SwitchPrefix="--" |
||||
|
Order="200"> |
||||
|
<Rule.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Bison" /> |
||||
|
</Rule.DataSource> |
||||
|
<Rule.Categories> |
||||
|
<Category |
||||
|
Name="General"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>General</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Bison Options"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Bison Options</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Command Line" |
||||
|
Subtype="CommandLine"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Command Line</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
</Rule.Categories> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="OutputFile" |
||||
|
Category="Bison Options" |
||||
|
IsRequired="true" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Output File Name" |
||||
|
Description="Specify the file for the parser implementation file. --output=value" |
||||
|
Switch="output="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="DefinesFile" |
||||
|
Category="Bison Options" |
||||
|
Subtype="file" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Defines File Name" |
||||
|
Description="Pretend that %defines was specified, i.e., write an extra output file containing macro definitions for the token type names defined in the grammar, as well as a few other declarations. --defines=value" |
||||
|
Switch="defines="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Debug" |
||||
|
Category="Bison Options" |
||||
|
DisplayName="Debug" |
||||
|
Description="In the parser implementation file, define the macro YYDEBUG to 1 if it is not already defined, so that the debugging facilities are compiled. (--debug)" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Enabling-Traces.html#Enabling-Traces" |
||||
|
Switch="debug" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Verbose" |
||||
|
Category="Bison Options" |
||||
|
DisplayName="Verbose" |
||||
|
Description="Write an extra output file containing verbose descriptions of the parser states and what is done for each type of lookahead token in that state. (--verbose)" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Understanding.html#Understanding" |
||||
|
Switch="verbose" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="NoLines" |
||||
|
Category="Bison Options" |
||||
|
DisplayName="No lines" |
||||
|
Description="Don’t put any #line preprocessor commands in the parser implementation file. (--no-lines)" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
Switch="no-lines" /> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="FilePrefix" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="File Prefix" |
||||
|
Description="Pretend that %file-prefix was specified, i.e., specify prefix to use for all Bison output file names. --file-prefix=prefix" |
||||
|
Switch="file-prefix="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="GraphFile" |
||||
|
Category="Bison Options" |
||||
|
Subtype="file" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Graph File" |
||||
|
Description="Output a graphical representation of the parser’s automaton computed by Bison, in Graphviz DOT format. --graph=file" |
||||
|
Switch="graph="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<EnumProperty |
||||
|
Name="Warnings" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Warnings" |
||||
|
Description="Output warnings falling in category. (--warnings=category)"> |
||||
|
|
||||
|
<EnumValue |
||||
|
Name="midrule-values" |
||||
|
DisplayName="midrule-values" |
||||
|
Switch="warnings=midrule-values"/> |
||||
|
<EnumValue |
||||
|
Name="yacc" |
||||
|
DisplayName="yacc" |
||||
|
Switch="warnings=yacc"/> |
||||
|
<EnumValue |
||||
|
Name="conflicts-sr" |
||||
|
DisplayName="conflicts-sr" |
||||
|
Switch="warnings=conflicts-sr"/> |
||||
|
<EnumValue |
||||
|
Name="conflicts-rr" |
||||
|
DisplayName="conflicts-rr" |
||||
|
Switch="warnings=conflicts-rr"/> |
||||
|
<EnumValue |
||||
|
Name="other" |
||||
|
DisplayName="other" |
||||
|
Switch="warnings=other"/> |
||||
|
<EnumValue |
||||
|
Name="all" |
||||
|
DisplayName="all" |
||||
|
Switch="warnings=all"/> |
||||
|
<EnumValue |
||||
|
Name="none" |
||||
|
DisplayName="none" |
||||
|
Switch="warnings=none"/> |
||||
|
<EnumValue |
||||
|
Name="error" |
||||
|
DisplayName="error" |
||||
|
Switch="warnings=error"/> |
||||
|
</EnumProperty> |
||||
|
|
||||
|
<EnumProperty |
||||
|
Name="Report" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Report" |
||||
|
Description="Write an extra output file containing verbose description of the comma separated list of things. (--report=things)"> |
||||
|
|
||||
|
<EnumValue |
||||
|
Name="state" |
||||
|
DisplayName="state" |
||||
|
Switch="report=state"/> |
||||
|
<EnumValue |
||||
|
Name="itemset" |
||||
|
DisplayName="itemset" |
||||
|
Switch="report=itemset"/> |
||||
|
<EnumValue |
||||
|
Name="lookahead" |
||||
|
DisplayName="lookahead" |
||||
|
Switch="report=lookahead"/> |
||||
|
<EnumValue |
||||
|
Name="solved" |
||||
|
DisplayName="solved" |
||||
|
Switch="report=solved"/> |
||||
|
<EnumValue |
||||
|
Name="all" |
||||
|
DisplayName="all" |
||||
|
Switch="report=all"/> |
||||
|
<EnumValue |
||||
|
Name="none" |
||||
|
DisplayName="none" |
||||
|
Switch="report=none"/> |
||||
|
</EnumProperty> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="ReportFile" |
||||
|
Category="Bison Options" |
||||
|
HelpUrl="https://www.gnu.org/software/bison/manual/html_node/Bison-Options.html#Bison-Options" |
||||
|
DisplayName="Report File Name" |
||||
|
Description="Specify the file for the verbose description. --report-file=value" |
||||
|
Switch="report-file="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="Inputs" |
||||
|
Category="Command Line" |
||||
|
IsRequired="true" |
||||
|
Switch=" "> |
||||
|
<StringListProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Bison" |
||||
|
SourceType="Item" /> |
||||
|
</StringListProperty.DataSource> |
||||
|
</StringListProperty> |
||||
|
<StringProperty |
||||
|
Name="CommandLineTemplate" |
||||
|
DisplayName="Command Line" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<DynamicEnumProperty |
||||
|
Name="BisonBeforeTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute Before</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run before.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^BisonBeforeTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<DynamicEnumProperty |
||||
|
Name="BisonAfterTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute After</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run after.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^BisonAfterTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<StringListProperty |
||||
|
Name="Outputs" |
||||
|
DisplayName="Outputs" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringProperty |
||||
|
Name="ExecutionDescription" |
||||
|
DisplayName="Execution Description" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringListProperty |
||||
|
Name="AdditionalDependencies" |
||||
|
DisplayName="Additional Dependencies" |
||||
|
IncludeInCommandLine="False" |
||||
|
Visible="false" /> |
||||
|
<StringProperty |
||||
|
Subtype="AdditionalOptions" |
||||
|
Name="AdditionalOptions" |
||||
|
Category="Command Line"> |
||||
|
<StringProperty.DisplayName> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.DisplayName> |
||||
|
<StringProperty.Description> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.Description> |
||||
|
</StringProperty> |
||||
|
</Rule> |
||||
|
<ItemType |
||||
|
Name="Bison" |
||||
|
DisplayName="Bison files" /> |
||||
|
<FileExtension |
||||
|
Name="*.y" |
||||
|
ContentType="Bison" /> |
||||
|
<ContentType |
||||
|
Name="Bison" |
||||
|
DisplayName="Bison files" |
||||
|
ItemType="Bison" /> |
||||
|
<Rule |
||||
|
Name="Flex" |
||||
|
PageTemplate="tool" |
||||
|
DisplayName="Flex files" |
||||
|
SwitchPrefix="--" |
||||
|
Order="200"> |
||||
|
<Rule.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Flex" /> |
||||
|
</Rule.DataSource> |
||||
|
<Rule.Categories> |
||||
|
<Category |
||||
|
Name="General"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>General</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Flex Options"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Flex Options</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Command Line" |
||||
|
Subtype="CommandLine"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Command Line</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
</Rule.Categories> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="OutputFile" |
||||
|
Category="Flex Options" |
||||
|
IsRequired="true" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="Output File Name" |
||||
|
Description="Directs flex to write the scanner to the file ‘FILE’ instead of ‘lex.yy.c’. --outfile=value" |
||||
|
Switch="outfile="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="HeaderFile" |
||||
|
Category="Flex Options" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="Header File Name" |
||||
|
Description="Instructs flex to write a C header to ‘FILE’. This file contains function prototypes, extern variables, and types used by the scanner. Only the external API is exported by the header file. (--header-file=value)" |
||||
|
Switch="header-file="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="Prefix" |
||||
|
Category="Flex Options" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="Prefix" |
||||
|
Description="Changes the default ‘yy’ prefix used by flex for all globally-visible variable and function names to instead be ‘PREFIX’. For example, ‘--prefix=foo’ changes the name of yytext to footext. It also changes the name of the default output file from lex.yy.c to lex.foo.c. (--prefix=value)" |
||||
|
Switch="prefix="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Wincompat" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Windows compatibility mode" |
||||
|
Description="This option changes 'unistd.h' unix header with windows analog 'io.h' and replaces isatty/fileno functions to safe windows analogs _isatty/_fileno. (--wincompat)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="wincompat" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="CaseInsensitive" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Case-insensitive mode" |
||||
|
Description="Instructs flex to generate a case-insensitive scanner. The case of letters given in the flex input patterns will be ignored, and tokens in the input will be matched regardless of case. The matched text given in yytext will have the preserved case (i.e., it will not be folded). (--case-insensitive)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="case-insensitive" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="LexCompat" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Lex-compatibility mode" |
||||
|
Description="Turns on maximum compatibility with the original AT&T lex implementation. Note that this does not mean full compatibility. Use of this option costs a considerable amount of performance, and it cannot be used with the ‘--c++’, ‘--full’, ‘--fast’, ‘-Cf’, or ‘-CF’ options. (--lex-compat)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="lex-compat" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Stack" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Start Condition Stacks" |
||||
|
Description="Enables the use of start condition stacks. (--stack)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="stack" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="BisonBridge" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Bison Bridge Mode" |
||||
|
Description="Instructs flex to generate a C scanner that is meant to be called by a GNU bison parser. The scanner has minor API changes for bison compatibility. In particular, the declaration of yylex is modified to take an additional parameter, yylval. (--bison-bridge)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="bison-bridge" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Noline" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="No #line Directives" |
||||
|
Description="Instructs flex not to generate #line directives. (--noline)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="noline" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Reentrant" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Generate Reentrant Scanner" |
||||
|
Description="Instructs flex to generate a reentrant C scanner. The generated scanner may safely be used in a multi-threaded environment. The API for a reentrant scanner is different than for a non-reentrant scanner. (--reentrant)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="reentrant" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Cpp" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Generate C++ Scanner" |
||||
|
Description="Specifies that you want flex to generate a C++ scanner class. (--c++)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="c++" /> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="CppClassName" |
||||
|
Category="Flex Options" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="C++ Class Name" |
||||
|
Description="Only applies when generating a C++ scanner (the ‘--c++’ option). It informs flex that you have derived NAME as a subclass of yyFlexLexer, so flex will place your actions in the member function foo::yylex() instead of yyFlexLexer::yylex(). It also generates a yyFlexLexer::yylex() member function that emits a run-time error (by invoking yyFlexLexer::LexerError()) if called. (--yyclass=value)" |
||||
|
Switch="yyclass="[value]"" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Debug" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Debug Mode" |
||||
|
Description="Makes the generated scanner run in debug mode. Whenever a pattern is recognized and the global variable yy_flex_debug is non-zero (which is the default), the scanner will write to ‘stderr’ a line of the form: -accepting rule at line 53 ('the matched text'). (--debug)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="debug" /> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="Inputs" |
||||
|
Category="Command Line" |
||||
|
IsRequired="true" |
||||
|
Switch=" "> |
||||
|
<StringListProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Flex" |
||||
|
SourceType="Item" /> |
||||
|
</StringListProperty.DataSource> |
||||
|
</StringListProperty> |
||||
|
<StringProperty |
||||
|
Name="CommandLineTemplate" |
||||
|
DisplayName="Command Line" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<DynamicEnumProperty |
||||
|
Name="FlexBeforeTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute Before</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run before.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^FlexBeforeTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<DynamicEnumProperty |
||||
|
Name="FlexAfterTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute After</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run after.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^FlexAfterTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<StringListProperty |
||||
|
Name="Outputs" |
||||
|
DisplayName="Outputs" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringProperty |
||||
|
Name="ExecutionDescription" |
||||
|
DisplayName="Execution Description" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringListProperty |
||||
|
Name="AdditionalDependencies" |
||||
|
DisplayName="Additional Dependencies" |
||||
|
IncludeInCommandLine="False" |
||||
|
Visible="false" /> |
||||
|
<StringProperty |
||||
|
Subtype="AdditionalOptions" |
||||
|
Name="AdditionalOptions" |
||||
|
Category="Command Line"> |
||||
|
<StringProperty.DisplayName> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.DisplayName> |
||||
|
<StringProperty.Description> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.Description> |
||||
|
</StringProperty> |
||||
|
</Rule> |
||||
|
<ItemType |
||||
|
Name="Flex" |
||||
|
DisplayName="Flex files" /> |
||||
|
<FileExtension |
||||
|
Name="*.l" |
||||
|
ContentType="Flex" /> |
||||
|
<ContentType |
||||
|
Name="Flex" |
||||
|
DisplayName="Flex files" |
||||
|
ItemType="Flex" /> |
||||
|
</ProjectSchemaDefinitions> |
||||
@ -0,0 +1,23 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup |
||||
|
Condition="'$(FlexBeforeTargets)' == '' and '$(FlexAfterTargets)' == '' and '$(ConfigurationType)' != 'Makefile'"> |
||||
|
<FlexBeforeTargets>Midl</FlexBeforeTargets> |
||||
|
<FlexAfterTargets>CustomBuild</FlexAfterTargets> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup> |
||||
|
<FlexDependsOn |
||||
|
Condition="'$(ConfigurationType)' != 'Makefile'">_SelectedFiles;$(FlexDependsOn)</FlexDependsOn> |
||||
|
</PropertyGroup> |
||||
|
<ItemDefinitionGroup> |
||||
|
<Flex> |
||||
|
<OutputFile>%(Filename).flex.cpp</OutputFile> |
||||
|
<Wincompat>true</Wincompat> |
||||
|
<CommandLineTemplate> |
||||
|
start /B /WAIT /D "%(RootDir)%(Directory)" win_flex.exe [AllOptions] [AdditionalOptions] "%(Filename)%(Extension)" |
||||
|
exit /b %errorlevel%</CommandLineTemplate> |
||||
|
<Outputs>%(RootDir)%(Directory)%(OutputFile);</Outputs> |
||||
|
<ExecutionDescription>Process "%(Filename)%(Extension)" flex file</ExecutionDescription> |
||||
|
</Flex> |
||||
|
</ItemDefinitionGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,94 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<PropertyPageSchema |
||||
|
Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" /> |
||||
|
<AvailableItemName |
||||
|
Include="Flex"> |
||||
|
<Targets>FlexTarget</Targets> |
||||
|
</AvailableItemName> |
||||
|
</ItemGroup> |
||||
|
<UsingTask |
||||
|
TaskName="Flex" |
||||
|
TaskFactory="XamlTaskFactory" |
||||
|
AssemblyName="Microsoft.Build.Tasks.v4.0"> |
||||
|
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task> |
||||
|
</UsingTask> |
||||
|
<Target |
||||
|
Name="FlexTarget" |
||||
|
BeforeTargets="$(FlexBeforeTargets)" |
||||
|
AfterTargets="$(FlexAfterTargets)" |
||||
|
Condition="'@(Flex)' != ''" |
||||
|
DependsOnTargets="$(FlexDependsOn);ComputeFlexOutput" |
||||
|
Outputs="%(Flex.Outputs)" |
||||
|
Inputs="%(Flex.Identity);%(Flex.AdditionalDependencies);$(MSBuildProjectFile)"> |
||||
|
<ItemGroup |
||||
|
Condition="'@(SelectedFiles)' != ''"> |
||||
|
<Flex |
||||
|
Remove="@(Flex)" |
||||
|
Condition="'%(Identity)' != '@(SelectedFiles)'" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Flex_tlog |
||||
|
Include="%(Flex.Outputs)" |
||||
|
Condition="'%(Flex.Outputs)' != '' and '%(Flex.ExcludedFromBuild)' != 'true'"> |
||||
|
<Source>@(Flex, '|')</Source> |
||||
|
</Flex_tlog> |
||||
|
</ItemGroup> |
||||
|
<Message |
||||
|
Importance="High" |
||||
|
Text="%(Flex.ExecutionDescription)" /> |
||||
|
<WriteLinesToFile |
||||
|
Condition="'@(Flex_tlog)' != '' and '%(Flex_tlog.ExcludedFromBuild)' != 'true'" |
||||
|
File="$(IntDir)$(ProjectName).write.1.tlog" |
||||
|
Lines="^%(Flex_tlog.Source);@(Flex_tlog->'%(Fullpath)')" /> |
||||
|
<Flex |
||||
|
Condition="'@(Flex)' != '' and '%(Flex.ExcludedFromBuild)' != 'true'" |
||||
|
CommandLineTemplate="%(Flex.CommandLineTemplate)" |
||||
|
OutputFile="%(Flex.OutputFile)" |
||||
|
HeaderFile="%(Flex.HeaderFile)" |
||||
|
Prefix="%(Flex.Prefix)" |
||||
|
Wincompat="%(Flex.Wincompat)" |
||||
|
CaseInsensitive="%(Flex.CaseInsensitive)" |
||||
|
LexCompat="%(Flex.LexCompat)" |
||||
|
Stack="%(Flex.Stack)" |
||||
|
BisonBridge="%(Flex.BisonBridge)" |
||||
|
Noline="%(Flex.Noline)" |
||||
|
Reentrant="%(Flex.Reentrant)" |
||||
|
Cpp="%(Flex.Cpp)" |
||||
|
CppClassName="%(Flex.CppClassName)" |
||||
|
Debug="%(Flex.Debug)" |
||||
|
AdditionalOptions="%(Flex.AdditionalOptions)" |
||||
|
Inputs="%(Flex.Identity)" /> |
||||
|
</Target> |
||||
|
<PropertyGroup> |
||||
|
<ComputeLinkInputsTargets> |
||||
|
$(ComputeLinkInputsTargets); |
||||
|
ComputeFlexOutput; |
||||
|
</ComputeLinkInputsTargets> |
||||
|
<ComputeLibInputsTargets> |
||||
|
$(ComputeLibInputsTargets); |
||||
|
ComputeFlexOutput; |
||||
|
</ComputeLibInputsTargets> |
||||
|
</PropertyGroup> |
||||
|
<Target |
||||
|
Name="ComputeFlexOutput" |
||||
|
Condition="'@(Flex)' != ''"> |
||||
|
<ItemGroup> |
||||
|
<FlexDirsToMake |
||||
|
Condition="'@(Flex)' != '' and '%(Flex.ExcludedFromBuild)' != 'true'" |
||||
|
Include="%(Flex.Outputs)" /> |
||||
|
<Link |
||||
|
Include="%(FlexDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<Lib |
||||
|
Include="%(FlexDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
<ImpLib |
||||
|
Include="%(FlexDirsToMake.Identity)" |
||||
|
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" /> |
||||
|
</ItemGroup> |
||||
|
<MakeDir |
||||
|
Directories="@(FlexDirsToMake->'%(RootDir)%(Directory)')" /> |
||||
|
</Target> |
||||
|
</Project> |
||||
@ -0,0 +1,243 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<ProjectSchemaDefinitions xmlns="clr-namespace:Microsoft.Build.Framework.XamlTypes;assembly=Microsoft.Build.Framework" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:transformCallback="Microsoft.Cpp.Dev10.ConvertPropertyCallback"> |
||||
|
<Rule |
||||
|
Name="Flex" |
||||
|
PageTemplate="tool" |
||||
|
DisplayName="Flex files" |
||||
|
SwitchPrefix="--" |
||||
|
Order="200"> |
||||
|
<Rule.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Flex" /> |
||||
|
</Rule.DataSource> |
||||
|
<Rule.Categories> |
||||
|
<Category |
||||
|
Name="General"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>General</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Flex Options"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Flex Options</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
<Category |
||||
|
Name="Command Line" |
||||
|
Subtype="CommandLine"> |
||||
|
<Category.DisplayName> |
||||
|
<sys:String>Command Line</sys:String> |
||||
|
</Category.DisplayName> |
||||
|
</Category> |
||||
|
</Rule.Categories> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="OutputFile" |
||||
|
Category="Flex Options" |
||||
|
IsRequired="true" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="Output File Name" |
||||
|
Description="Directs flex to write the scanner to the file ‘FILE’ instead of ‘lex.yy.c’. --outfile=value" |
||||
|
Switch="outfile="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="HeaderFile" |
||||
|
Category="Flex Options" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="Header File Name" |
||||
|
Description="Instructs flex to write a C header to ‘FILE’. This file contains function prototypes, extern variables, and types used by the scanner. Only the external API is exported by the header file. (--header-file=value)" |
||||
|
Switch="header-file="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="Prefix" |
||||
|
Category="Flex Options" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="Prefix" |
||||
|
Description="Changes the default ‘yy’ prefix used by flex for all globally-visible variable and function names to instead be ‘PREFIX’. For example, ‘--prefix=foo’ changes the name of yytext to footext. It also changes the name of the default output file from lex.yy.c to lex.foo.c. (--prefix=value)" |
||||
|
Switch="prefix="[value]"" |
||||
|
/> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Wincompat" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Windows compatibility mode" |
||||
|
Description="This option changes 'unistd.h' unix header with windows analog 'io.h' and replaces isatty/fileno functions to safe windows analogs _isatty/_fileno. (--wincompat)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="wincompat" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="CaseInsensitive" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Case-insensitive mode" |
||||
|
Description="Instructs flex to generate a case-insensitive scanner. The case of letters given in the flex input patterns will be ignored, and tokens in the input will be matched regardless of case. The matched text given in yytext will have the preserved case (i.e., it will not be folded). (--case-insensitive)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="case-insensitive" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="LexCompat" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Lex-compatibility mode" |
||||
|
Description="Turns on maximum compatibility with the original AT&T lex implementation. Note that this does not mean full compatibility. Use of this option costs a considerable amount of performance, and it cannot be used with the ‘--c++’, ‘--full’, ‘--fast’, ‘-Cf’, or ‘-CF’ options. (--lex-compat)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="lex-compat" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Stack" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Start Condition Stacks" |
||||
|
Description="Enables the use of start condition stacks. (--stack)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="stack" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="BisonBridge" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Bison Bridge Mode" |
||||
|
Description="Instructs flex to generate a C scanner that is meant to be called by a GNU bison parser. The scanner has minor API changes for bison compatibility. In particular, the declaration of yylex is modified to take an additional parameter, yylval. (--bison-bridge)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="bison-bridge" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Noline" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="No #line Directives" |
||||
|
Description="Instructs flex not to generate #line directives. (--noline)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="noline" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Reentrant" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Generate Reentrant Scanner" |
||||
|
Description="Instructs flex to generate a reentrant C scanner. The generated scanner may safely be used in a multi-threaded environment. The API for a reentrant scanner is different than for a non-reentrant scanner. (--reentrant)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="reentrant" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Cpp" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Generate C++ Scanner" |
||||
|
Description="Specifies that you want flex to generate a C++ scanner class. (--c++)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="c++" /> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="CppClassName" |
||||
|
Category="Flex Options" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
DisplayName="C++ Class Name" |
||||
|
Description="Only applies when generating a C++ scanner (the ‘--c++’ option). It informs flex that you have derived NAME as a subclass of yyFlexLexer, so flex will place your actions in the member function foo::yylex() instead of yyFlexLexer::yylex(). It also generates a yyFlexLexer::yylex() member function that emits a run-time error (by invoking yyFlexLexer::LexerError()) if called. (--yyclass=value)" |
||||
|
Switch="yyclass="[value]"" /> |
||||
|
|
||||
|
<BoolProperty |
||||
|
Name="Debug" |
||||
|
Category="Flex Options" |
||||
|
DisplayName="Debug Mode" |
||||
|
Description="Makes the generated scanner run in debug mode. Whenever a pattern is recognized and the global variable yy_flex_debug is non-zero (which is the default), the scanner will write to ‘stderr’ a line of the form: -accepting rule at line 53 ('the matched text'). (--debug)" |
||||
|
HelpUrl="https://westes.github.io/flex/manual/Scanner-Options.html#Scanner-Options" |
||||
|
Switch="debug" /> |
||||
|
|
||||
|
<StringListProperty |
||||
|
Name="Inputs" |
||||
|
Category="Command Line" |
||||
|
IsRequired="true" |
||||
|
Switch=" "> |
||||
|
<StringListProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="Flex" |
||||
|
SourceType="Item" /> |
||||
|
</StringListProperty.DataSource> |
||||
|
</StringListProperty> |
||||
|
<StringProperty |
||||
|
Name="CommandLineTemplate" |
||||
|
DisplayName="Command Line" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<DynamicEnumProperty |
||||
|
Name="FlexBeforeTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute Before</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run before.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^FlexBeforeTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<DynamicEnumProperty |
||||
|
Name="FlexAfterTargets" |
||||
|
Category="General" |
||||
|
EnumProvider="Targets" |
||||
|
IncludeInCommandLine="False"> |
||||
|
<DynamicEnumProperty.DisplayName> |
||||
|
<sys:String>Execute After</sys:String> |
||||
|
</DynamicEnumProperty.DisplayName> |
||||
|
<DynamicEnumProperty.Description> |
||||
|
<sys:String>Specifies the targets for the build customization to run after.</sys:String> |
||||
|
</DynamicEnumProperty.Description> |
||||
|
<DynamicEnumProperty.ProviderSettings> |
||||
|
<NameValuePair |
||||
|
Name="Exclude" |
||||
|
Value="^FlexAfterTargets|^Compute" /> |
||||
|
</DynamicEnumProperty.ProviderSettings> |
||||
|
<DynamicEnumProperty.DataSource> |
||||
|
<DataSource |
||||
|
Persistence="ProjectFile" |
||||
|
ItemType="" |
||||
|
HasConfigurationCondition="true" /> |
||||
|
</DynamicEnumProperty.DataSource> |
||||
|
</DynamicEnumProperty> |
||||
|
<StringListProperty |
||||
|
Name="Outputs" |
||||
|
DisplayName="Outputs" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringProperty |
||||
|
Name="ExecutionDescription" |
||||
|
DisplayName="Execution Description" |
||||
|
Visible="False" |
||||
|
IncludeInCommandLine="False" /> |
||||
|
<StringListProperty |
||||
|
Name="AdditionalDependencies" |
||||
|
DisplayName="Additional Dependencies" |
||||
|
IncludeInCommandLine="False" |
||||
|
Visible="false" /> |
||||
|
<StringProperty |
||||
|
Subtype="AdditionalOptions" |
||||
|
Name="AdditionalOptions" |
||||
|
Category="Command Line"> |
||||
|
<StringProperty.DisplayName> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.DisplayName> |
||||
|
<StringProperty.Description> |
||||
|
<sys:String>Additional Options</sys:String> |
||||
|
</StringProperty.Description> |
||||
|
</StringProperty> |
||||
|
</Rule> |
||||
|
<ItemType |
||||
|
Name="Flex" |
||||
|
DisplayName="Flex files" /> |
||||
|
<FileExtension |
||||
|
Name="*.l" |
||||
|
ContentType="Flex" /> |
||||
|
<ContentType |
||||
|
Name="Flex" |
||||
|
DisplayName="Flex files" |
||||
|
ItemType="Flex" /> |
||||
|
</ProjectSchemaDefinitions> |
||||
@ -0,0 +1,227 @@ |
|||||
|
This directory contains data needed by Bison. |
||||
|
|
||||
|
# Directory Content |
||||
|
## Skeletons |
||||
|
Bison skeletons: the general shapes of the different parser kinds, that are |
||||
|
specialized for specific grammars by the bison program. |
||||
|
|
||||
|
Currently, the supported skeletons are: |
||||
|
|
||||
|
- yacc.c |
||||
|
It used to be named bison.simple: it corresponds to C Yacc |
||||
|
compatible LALR(1) parsers. |
||||
|
|
||||
|
- lalr1.cc |
||||
|
Produces a C++ parser class. |
||||
|
|
||||
|
- lalr1.java |
||||
|
Produces a Java parser class. |
||||
|
|
||||
|
- glr.c |
||||
|
A Generalized LR C parser based on Bison's LALR(1) tables. |
||||
|
|
||||
|
- glr.cc |
||||
|
A Generalized LR C++ parser. Actually a C++ wrapper around glr.c. |
||||
|
|
||||
|
These skeletons are the only ones supported by the Bison team. Because the |
||||
|
interface between skeletons and the bison program is not finished, *we are |
||||
|
not bound to it*. In particular, Bison is not mature enough for us to |
||||
|
consider that "foreign skeletons" are supported. |
||||
|
|
||||
|
## m4sugar |
||||
|
This directory contains M4sugar, sort of an extended library for M4, which |
||||
|
is used by Bison to instantiate the skeletons. |
||||
|
|
||||
|
## xslt |
||||
|
This directory contains XSLT programs that transform Bison's XML output into |
||||
|
various formats. |
||||
|
|
||||
|
- bison.xsl |
||||
|
A library of routines used by the other XSLT programs. |
||||
|
|
||||
|
- xml2dot.xsl |
||||
|
Conversion into GraphViz's dot format. |
||||
|
|
||||
|
- xml2text.xsl |
||||
|
Conversion into text. |
||||
|
|
||||
|
- xml2xhtml.xsl |
||||
|
Conversion into XHTML. |
||||
|
|
||||
|
# Implementation Notes About the Skeletons |
||||
|
|
||||
|
"Skeleton" in Bison parlance means "backend": a skeleton is fed by the bison |
||||
|
executable with LR tables, facts about the symbols, etc. and they generate |
||||
|
the output (say parser.cc, parser.hh, location.hh, etc.). They are only in |
||||
|
charge of generating the parser and its auxiliary files, they do not |
||||
|
generate the XML output, the parser.output reports, nor the graphical |
||||
|
rendering. |
||||
|
|
||||
|
The bits of information passing from bison to the backend is named |
||||
|
"muscles". Muscles are passed to M4 via its standard input: it's a set of |
||||
|
m4 definitions. To see them, use `--trace=muscles`. |
||||
|
|
||||
|
Except for muscles, whose names are generated by bison, the skeletons have |
||||
|
no constraint at all on the macro names: there is no technical/theoretical |
||||
|
limitation, as long as you generate the output, you can do what you want. |
||||
|
However, of course, that would be a bad idea if, say, the C and C++ |
||||
|
skeletons used different approaches and had completely different |
||||
|
implementations. That would be a maintenance nightmare. |
||||
|
|
||||
|
Below, we document some of the macros that we use in several of the |
||||
|
skeletons. If you are to write a new skeleton, please, implement them for |
||||
|
your language. Overall, be sure to follow the same patterns as the existing |
||||
|
skeletons. |
||||
|
|
||||
|
## Vocabulary |
||||
|
|
||||
|
We use "formal arguments", or "formals" for short, to denote the declared |
||||
|
parameters of a function (e.g., `int argc, const char **argv`). Yes, this |
||||
|
is somewhat contradictory with `param` in the `%param` directives. |
||||
|
|
||||
|
We use "effective arguments", or "args" for short, to denote the values |
||||
|
passed in function calls (e.g., `argc, argv`). |
||||
|
|
||||
|
## Symbols |
||||
|
|
||||
|
### `b4_symbol(NUM, FIELD)` |
||||
|
In order to unify the handling of the various aspects of symbols (tag, type |
||||
|
name, whether terminal, etc.), bison.exe defines one macro per (token, |
||||
|
field), where field can `has_id`, `id`, etc.: see |
||||
|
`prepare_symbol_definitions()` in `src/output.c`. |
||||
|
|
||||
|
NUM can be: |
||||
|
- `empty` to denote the "empty" pseudo-symbol when it exists, |
||||
|
- `eof`, `error`, or `undef` |
||||
|
- a symbol number. |
||||
|
|
||||
|
FIELD can be: |
||||
|
|
||||
|
- `has_id`: 0 or 1 |
||||
|
Whether the symbol has an `id`. |
||||
|
|
||||
|
- `id`: string (e.g., `exp`, `NUM`, or `TOK_NUM` with api.token.prefix) |
||||
|
If `has_id`, the name of the token kind (prefixed by api.token.prefix if |
||||
|
defined), otherwise empty. Guaranteed to be usable as a C identifier. |
||||
|
This is used to define the token kind (i.e., the enum used by the return |
||||
|
value of yylex). Should be named `token_kind`. |
||||
|
|
||||
|
- `tag`: string |
||||
|
A human readable representation of the symbol. Can be `'foo'`, |
||||
|
`'foo.id'`, `'"foo"'` etc. |
||||
|
|
||||
|
- `code`: integer |
||||
|
The token code associated to the token kind `id`. |
||||
|
The external number as used by yylex. Can be ASCII code when a character, |
||||
|
some number chosen by bison, or some user number in the case of `%token |
||||
|
FOO <NUM>`. Corresponds to `yychar` in `yacc.c`. |
||||
|
|
||||
|
- `is_token`: 0 or 1 |
||||
|
Whether this is a terminal symbol. |
||||
|
|
||||
|
- `kind_base`: string (e.g., `YYSYMBOL_exp`, `YYSYMBOL_NUM`) |
||||
|
The base of the symbol kind, i.e., the enumerator of this symbol (token or |
||||
|
nonterminal) which is mapped to its `number`. |
||||
|
|
||||
|
- `kind`: string |
||||
|
Same as `kind_base`, but possibly with a prefix in some languages. E.g., |
||||
|
EOF's `kind_base` and `kind` are `YYSYMBOL_YYEOF` in C, but are |
||||
|
`S_YYEMPTY` and `symbol_kind::S_YYEMPTY` in C++. |
||||
|
|
||||
|
- `number`: integer |
||||
|
The code associated to the `kind`. |
||||
|
The internal number (computed from the external number by yytranslate). |
||||
|
Corresponds to yytoken in yacc.c. This is the same number that serves as |
||||
|
key in b4_symbol(NUM, FIELD). |
||||
|
|
||||
|
In bison, symbols are first assigned increasing numbers in order of |
||||
|
appearance (but tokens first, then nterms). After grammar reduction, |
||||
|
unused nterms are then renumbered to appear last (i.e., first tokens, then |
||||
|
used nterms and finally unused nterms). This final number NUM is the one |
||||
|
contained in this field, and it is the one used as key in `b4_symbol(NUM, |
||||
|
FIELD)`. |
||||
|
|
||||
|
The code of the rule actions, however, is emitted before we know what |
||||
|
symbols are unused, so they use the original numbers. To avoid confusion, |
||||
|
they actually use "orig NUM" instead of just "NUM". bison also emits |
||||
|
definitions for `b4_symbol(orig NUM, number)` that map from original |
||||
|
numbers to the new ones. `b4_symbol` actually resolves `orig NUM` in the |
||||
|
other case, i.e., `b4_symbol(orig 42, tag)` would return the tag of the |
||||
|
symbols whose original number was 42. |
||||
|
|
||||
|
- `has_type`: 0, 1 |
||||
|
Whether has a semantic value. |
||||
|
|
||||
|
- `type_tag`: string |
||||
|
When api.value.type=union, the generated name for the union member. |
||||
|
yytype_INT etc. for symbols that has_id, otherwise yytype_1 etc. |
||||
|
|
||||
|
- `type`: string |
||||
|
If it has a semantic value, its type tag, or, if variant are used, |
||||
|
its type. |
||||
|
In the case of api.value.type=union, type is the real type (e.g. int). |
||||
|
|
||||
|
- `slot`: string |
||||
|
If it has a semantic value, the name of the union member (i.e., bounces to |
||||
|
either `type_tag` or `type`). It would be better to fix our mess and |
||||
|
always use `type` for the true type of the member, and `type_tag` for the |
||||
|
name of the union member. |
||||
|
|
||||
|
- `has_printer`: 0, 1 |
||||
|
- `printer`: string |
||||
|
- `printer_file`: string |
||||
|
- `printer_line`: integer |
||||
|
- `printer_loc`: location |
||||
|
If the symbol has a printer, everything about it. |
||||
|
|
||||
|
- `has_destructor`, `destructor`, `destructor_file`, `destructor_line`, `destructor_loc` |
||||
|
Likewise. |
||||
|
|
||||
|
### `b4_symbol_value(VAL, [SYMBOL-NUM], [TYPE-TAG])` |
||||
|
Expansion of $$, $1, $<TYPE-TAG>3, etc. |
||||
|
|
||||
|
The semantic value from a given VAL. |
||||
|
- `VAL`: some semantic value storage (typically a union). e.g., `yylval` |
||||
|
- `SYMBOL-NUM`: the symbol number from which we extract the type tag. |
||||
|
- `TYPE-TAG`, the user forced the `<TYPE-TAG>`. |
||||
|
|
||||
|
The result can be used safely, it is put in parens to avoid nasty precedence |
||||
|
issues. |
||||
|
|
||||
|
### `b4_lhs_value(SYMBOL-NUM, [TYPE])` |
||||
|
Expansion of `$$` or `$<TYPE>$`, for symbol `SYMBOL-NUM`. |
||||
|
|
||||
|
### `b4_rhs_data(RULE-LENGTH, POS)` |
||||
|
The data corresponding to the symbol `#POS`, where the current rule has |
||||
|
`RULE-LENGTH` symbols on RHS. |
||||
|
|
||||
|
### `b4_rhs_value(RULE-LENGTH, POS, SYMBOL-NUM, [TYPE])` |
||||
|
Expansion of `$<TYPE>POS`, where the current rule has `RULE-LENGTH` symbols |
||||
|
on RHS. |
||||
|
|
||||
|
<!-- |
||||
|
|
||||
|
Local Variables: |
||||
|
mode: markdown |
||||
|
fill-column: 76 |
||||
|
ispell-dictionary: "american" |
||||
|
End: |
||||
|
|
||||
|
Copyright (C) 2002, 2008-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
This file is part of GNU Bison. |
||||
|
|
||||
|
This program is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
--> |
||||
@ -0,0 +1,61 @@ |
|||||
|
/* Default styling rules for Bison when doing terminal output. |
||||
|
Copyright (C) 2019-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
This program is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation; either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
||||
|
|
||||
|
/* This is an experimental feature. The class names may change in the |
||||
|
future. */ |
||||
|
|
||||
|
/* Diagnostics. */ |
||||
|
.warning { color: purple; } |
||||
|
.error { color: red; } |
||||
|
.note { color: cyan; } |
||||
|
|
||||
|
.fixit-insert { color: green; } |
||||
|
|
||||
|
/* Semantic values in Bison's own parser traces. */ |
||||
|
.value { color: green; } |
||||
|
|
||||
|
/* "Sections" in traces (--trace). */ |
||||
|
.trace0 { color: green; } |
||||
|
|
||||
|
/* Syntax error messages. */ |
||||
|
.expected { color: green; } |
||||
|
.unexpected { color: red; } |
||||
|
|
||||
|
|
||||
|
/* Counterexamples. */ |
||||
|
|
||||
|
/* Cex: point in rule. */ |
||||
|
.cex-dot { color: red; } |
||||
|
|
||||
|
/* Cex: coloring various rules. */ |
||||
|
.cex-0 { color: yellow; } |
||||
|
.cex-1 { color: green; } |
||||
|
.cex-2 { color: blue; } |
||||
|
.cex-3 { color: purple; } |
||||
|
.cex-4 { color: violet; } |
||||
|
.cex-5 { color: orange; } |
||||
|
.cex-6 { color: brown; } |
||||
|
.cex-7 { color: mauve; } |
||||
|
.cex-8 { color: #013220; } /* Dark green. */ |
||||
|
.cex-9 { color: #e75480; } /* Dark pink. */ |
||||
|
.cex-10 { color: cyan; } |
||||
|
.cex-11 { color: orange; } |
||||
|
|
||||
|
/* Cex: derivation rewriting steps. */ |
||||
|
.cex-step { font-style: italic; } |
||||
|
|
||||
|
/* Cex: leaves of a derivation. */ |
||||
|
.cex-leaf { font-weight: 600; } |
||||
@ -0,0 +1,58 @@ |
|||||
|
## Copyright (C) 2002, 2005-2015, 2018-2021 Free Software Foundation,
|
||||
|
## Inc.
|
||||
|
|
||||
|
## This program is free software: you can redistribute it and/or modify
|
||||
|
## it under the terms of the GNU General Public License as published by
|
||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||
|
## (at your option) any later version.
|
||||
|
##
|
||||
|
## This program is distributed in the hope that it will be useful,
|
||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
## GNU General Public License for more details.
|
||||
|
##
|
||||
|
## You should have received a copy of the GNU General Public License
|
||||
|
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
dist_pkgdata_DATA = \
|
||||
|
data/README.md \
|
||||
|
data/bison-default.css |
||||
|
|
||||
|
skeletonsdir = $(pkgdatadir)/skeletons |
||||
|
dist_skeletons_DATA = \
|
||||
|
data/skeletons/bison.m4 \
|
||||
|
data/skeletons/c++-skel.m4 \
|
||||
|
data/skeletons/c++.m4 \
|
||||
|
data/skeletons/c-like.m4 \
|
||||
|
data/skeletons/c-skel.m4 \
|
||||
|
data/skeletons/c.m4 \
|
||||
|
data/skeletons/glr.c \
|
||||
|
data/skeletons/glr.cc \
|
||||
|
data/skeletons/glr2.cc \
|
||||
|
data/skeletons/java-skel.m4 \
|
||||
|
data/skeletons/java.m4 \
|
||||
|
data/skeletons/lalr1.cc \
|
||||
|
data/skeletons/lalr1.java \
|
||||
|
data/skeletons/location.cc \
|
||||
|
data/skeletons/stack.hh \
|
||||
|
data/skeletons/traceon.m4 \
|
||||
|
data/skeletons/variant.hh \
|
||||
|
data/skeletons/yacc.c |
||||
|
|
||||
|
# Experimental support for the D language.
|
||||
|
dist_skeletons_DATA += \
|
||||
|
data/skeletons/d-skel.m4 \
|
||||
|
data/skeletons/d.m4 \
|
||||
|
data/skeletons/lalr1.d |
||||
|
|
||||
|
m4sugardir = $(pkgdatadir)/m4sugar |
||||
|
dist_m4sugar_DATA = \
|
||||
|
data/m4sugar/foreach.m4 \
|
||||
|
data/m4sugar/m4sugar.m4 |
||||
|
|
||||
|
xsltdir = $(pkgdatadir)/xslt |
||||
|
dist_xslt_DATA = \
|
||||
|
data/xslt/bison.xsl \
|
||||
|
data/xslt/xml2dot.xsl \
|
||||
|
data/xslt/xml2text.xsl \
|
||||
|
data/xslt/xml2xhtml.xsl |
||||
@ -0,0 +1,362 @@ |
|||||
|
# -*- Autoconf -*- |
||||
|
# This file is part of Autoconf. |
||||
|
# foreach-based replacements for recursive functions. |
||||
|
# Speeds up GNU M4 1.4.x by avoiding quadratic $@ recursion, but penalizes |
||||
|
# GNU M4 1.6 by requiring more memory and macro expansions. |
||||
|
# |
||||
|
# Copyright (C) 2008-2017, 2020 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This file is part of Autoconf. This program is free |
||||
|
# software; you can redistribute it and/or modify it under the |
||||
|
# terms of the GNU General Public License as published by the |
||||
|
# Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# Under Section 7 of GPL version 3, you are granted additional |
||||
|
# permissions described in the Autoconf Configure Script Exception, |
||||
|
# version 3.0, as published by the Free Software Foundation. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# and a copy of the Autoconf Configure Script Exception along with |
||||
|
# this program; see the files COPYINGv3 and COPYING.EXCEPTION |
||||
|
# respectively. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
# Written by Eric Blake. |
||||
|
|
||||
|
# In M4 1.4.x, every byte of $@ is rescanned. This means that an |
||||
|
# algorithm on n arguments that recurses with one less argument each |
||||
|
# iteration will scan n * (n + 1) / 2 arguments, for O(n^2) time. In |
||||
|
# M4 1.6, this was fixed so that $@ is only scanned once, then |
||||
|
# back-references are made to information stored about the scan. |
||||
|
# Thus, n iterations need only scan n arguments, for O(n) time. |
||||
|
# Additionally, in M4 1.4.x, recursive algorithms did not clean up |
||||
|
# memory very well, requiring O(n^2) memory rather than O(n) for n |
||||
|
# iterations. |
||||
|
# |
||||
|
# This file is designed to overcome the quadratic nature of $@ |
||||
|
# recursion by writing a variant of m4_foreach that uses m4_for rather |
||||
|
# than $@ recursion to operate on the list. This involves more macro |
||||
|
# expansions, but avoids the need to rescan a quadratic number of |
||||
|
# arguments, making these replacements very attractive for M4 1.4.x. |
||||
|
# On the other hand, in any version of M4, expanding additional macros |
||||
|
# costs additional time; therefore, in M4 1.6, where $@ recursion uses |
||||
|
# fewer macros, these replacements actually pessimize performance. |
||||
|
# Additionally, the use of $10 to mean the tenth argument violates |
||||
|
# POSIX; although all versions of m4 1.4.x support this meaning, a |
||||
|
# future m4 version may switch to take it as the first argument |
||||
|
# concatenated with a literal 0, so the implementations in this file |
||||
|
# are not future-proof. Thus, this file is conditionally included as |
||||
|
# part of m4_init(), only when it is detected that M4 probably has |
||||
|
# quadratic behavior (ie. it lacks the macro __m4_version__). |
||||
|
# |
||||
|
# Please keep this file in sync with m4sugar.m4. |
||||
|
|
||||
|
# _m4_foreach(PRE, POST, IGNORED, ARG...) |
||||
|
# --------------------------------------- |
||||
|
# Form the common basis of the m4_foreach and m4_map macros. For each |
||||
|
# ARG, expand PRE[ARG]POST[]. The IGNORED argument makes recursion |
||||
|
# easier, and must be supplied rather than implicit. |
||||
|
# |
||||
|
# This version minimizes the number of times that $@ is evaluated by |
||||
|
# using m4_for to generate a boilerplate into _m4_f then passing $@ to |
||||
|
# that temporary macro. Thus, the recursion is done in m4_for without |
||||
|
# reparsing any user input, and is not quadratic. For an idea of how |
||||
|
# this works, note that m4_foreach(i,[1,2],[i]) calls |
||||
|
# _m4_foreach([m4_define([i],],[)i],[],[1],[2]) |
||||
|
# which defines _m4_f: |
||||
|
# $1[$4]$2[]$1[$5]$2[]_m4_popdef([_m4_f]) |
||||
|
# then calls _m4_f([m4_define([i],],[)i],[],[1],[2]) for a net result: |
||||
|
# m4_define([i],[1])i[]m4_define([i],[2])i[]_m4_popdef([_m4_f]). |
||||
|
m4_define([_m4_foreach], |
||||
|
[m4_if([$#], [3], [], |
||||
|
[m4_pushdef([_m4_f], _m4_for([4], [$#], [1], |
||||
|
[$0_([1], [2],], [)])[_m4_popdef([_m4_f])])_m4_f($@)])]) |
||||
|
|
||||
|
m4_define([_m4_foreach_], |
||||
|
[[$$1[$$3]$$2[]]]) |
||||
|
|
||||
|
# m4_case(SWITCH, VAL1, IF-VAL1, VAL2, IF-VAL2, ..., DEFAULT) |
||||
|
# ----------------------------------------------------------- |
||||
|
# Find the first VAL that SWITCH matches, and expand the corresponding |
||||
|
# IF-VAL. If there are no matches, expand DEFAULT. |
||||
|
# |
||||
|
# Use m4_for to create a temporary macro in terms of a boilerplate |
||||
|
# m4_if with final cleanup. If $# is even, we have DEFAULT; if it is |
||||
|
# odd, then rounding the last $# up in the temporary macro is |
||||
|
# harmless. For example, both m4_case(1,2,3,4,5) and |
||||
|
# m4_case(1,2,3,4,5,6) result in the intermediate _m4_case being |
||||
|
# m4_if([$1],[$2],[$3],[$1],[$4],[$5],_m4_popdef([_m4_case])[$6]) |
||||
|
m4_define([m4_case], |
||||
|
[m4_if(m4_eval([$# <= 2]), [1], [$2], |
||||
|
[m4_pushdef([_$0], [m4_if(]_m4_for([2], m4_eval([($# - 1) / 2 * 2]), [2], |
||||
|
[_$0_(], [)])[_m4_popdef( |
||||
|
[_$0])]m4_dquote($m4_eval([($# + 1) & ~1]))[)])_$0($@)])]) |
||||
|
|
||||
|
m4_define([_m4_case_], |
||||
|
[$0_([1], [$1], m4_incr([$1]))]) |
||||
|
|
||||
|
m4_define([_m4_case__], |
||||
|
[[[$$1],[$$2],[$$3],]]) |
||||
|
|
||||
|
# m4_bmatch(SWITCH, RE1, VAL1, RE2, VAL2, ..., DEFAULT) |
||||
|
# ----------------------------------------------------- |
||||
|
# m4 equivalent of |
||||
|
# |
||||
|
# if (SWITCH =~ RE1) |
||||
|
# VAL1; |
||||
|
# elif (SWITCH =~ RE2) |
||||
|
# VAL2; |
||||
|
# elif ... |
||||
|
# ... |
||||
|
# else |
||||
|
# DEFAULT |
||||
|
# |
||||
|
# We build the temporary macro _m4_b: |
||||
|
# m4_define([_m4_b], _m4_defn([_m4_bmatch]))_m4_b([$1], [$2], [$3])... |
||||
|
# _m4_b([$1], [$m-1], [$m])_m4_b([], [], [$m+1]_m4_popdef([_m4_b])) |
||||
|
# then invoke m4_unquote(_m4_b($@)), for concatenation with later text. |
||||
|
m4_define([m4_bmatch], |
||||
|
[m4_if([$#], 0, [m4_fatal([$0: too few arguments: $#])], |
||||
|
[$#], 1, [m4_fatal([$0: too few arguments: $#: $1])], |
||||
|
[$#], 2, [$2], |
||||
|
[m4_pushdef([_m4_b], [m4_define([_m4_b], |
||||
|
_m4_defn([_$0]))]_m4_for([3], m4_eval([($# + 1) / 2 * 2 - 1]), |
||||
|
[2], [_$0_(], [)])[_m4_b([], [],]m4_dquote([$]m4_eval( |
||||
|
[($# + 1) / 2 * 2]))[_m4_popdef([_m4_b]))])m4_unquote(_m4_b($@))])]) |
||||
|
|
||||
|
m4_define([_m4_bmatch], |
||||
|
[m4_if(m4_bregexp([$1], [$2]), [-1], [], [[$3]m4_define([$0])])]) |
||||
|
|
||||
|
m4_define([_m4_bmatch_], |
||||
|
[$0_([1], m4_decr([$1]), [$1])]) |
||||
|
|
||||
|
m4_define([_m4_bmatch__], |
||||
|
[[_m4_b([$$1], [$$2], [$$3])]]) |
||||
|
|
||||
|
|
||||
|
# m4_cond(TEST1, VAL1, IF-VAL1, TEST2, VAL2, IF-VAL2, ..., [DEFAULT]) |
||||
|
# ------------------------------------------------------------------- |
||||
|
# Similar to m4_if, except that each TEST is expanded when encountered. |
||||
|
# If the expansion of TESTn matches the string VALn, the result is IF-VALn. |
||||
|
# The result is DEFAULT if no tests passed. This macro allows |
||||
|
# short-circuiting of expensive tests, where it pays to arrange quick |
||||
|
# filter tests to run first. |
||||
|
# |
||||
|
# m4_cond already guarantees either 3*n or 3*n + 1 arguments, 1 <= n. |
||||
|
# We only have to speed up _m4_cond, by building the temporary _m4_c: |
||||
|
# m4_define([_m4_c], _m4_defn([m4_unquote]))_m4_c([m4_if(($1), [($2)], |
||||
|
# [[$3]m4_define([_m4_c])])])_m4_c([m4_if(($4), [($5)], |
||||
|
# [[$6]m4_define([_m4_c])])])..._m4_c([m4_if(($m-2), [($m-1)], |
||||
|
# [[$m]m4_define([_m4_c])])])_m4_c([[$m+1]]_m4_popdef([_m4_c])) |
||||
|
# We invoke m4_unquote(_m4_c($@)), for concatenation with later text. |
||||
|
m4_define([_m4_cond], |
||||
|
[m4_pushdef([_m4_c], [m4_define([_m4_c], |
||||
|
_m4_defn([m4_unquote]))]_m4_for([2], m4_eval([$# / 3 * 3 - 1]), [3], |
||||
|
[$0_(], [)])[_m4_c(]m4_dquote(m4_dquote( |
||||
|
[$]m4_eval([$# / 3 * 3 + 1])))[_m4_popdef([_m4_c]))])m4_unquote(_m4_c($@))]) |
||||
|
|
||||
|
m4_define([_m4_cond_], |
||||
|
[$0_(m4_decr([$1]), [$1], m4_incr([$1]))]) |
||||
|
|
||||
|
m4_define([_m4_cond__], |
||||
|
[[_m4_c([m4_if(($$1), [($$2)], [[$$3]m4_define([_m4_c])])])]]) |
||||
|
|
||||
|
# m4_bpatsubsts(STRING, RE1, SUBST1, RE2, SUBST2, ...) |
||||
|
# ---------------------------------------------------- |
||||
|
# m4 equivalent of |
||||
|
# |
||||
|
# $_ = STRING; |
||||
|
# s/RE1/SUBST1/g; |
||||
|
# s/RE2/SUBST2/g; |
||||
|
# ... |
||||
|
# |
||||
|
# m4_bpatsubsts already validated an odd number of arguments; we only |
||||
|
# need to speed up _m4_bpatsubsts. To avoid nesting, we build the |
||||
|
# temporary _m4_p: |
||||
|
# m4_define([_m4_p], [$1])m4_define([_m4_p], |
||||
|
# m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$2], [$3]))m4_define([_m4_p], |
||||
|
# m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$4], [$5]))m4_define([_m4_p],... |
||||
|
# m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$m-1], [$m]))m4_unquote( |
||||
|
# _m4_defn([_m4_p])_m4_popdef([_m4_p])) |
||||
|
m4_define([_m4_bpatsubsts], |
||||
|
[m4_pushdef([_m4_p], [m4_define([_m4_p], |
||||
|
]m4_dquote([$]1)[)]_m4_for([3], [$#], [2], [$0_(], |
||||
|
[)])[m4_unquote(_m4_defn([_m4_p])_m4_popdef([_m4_p]))])_m4_p($@)]) |
||||
|
|
||||
|
m4_define([_m4_bpatsubsts_], |
||||
|
[$0_(m4_decr([$1]), [$1])]) |
||||
|
|
||||
|
m4_define([_m4_bpatsubsts__], |
||||
|
[[m4_define([_m4_p], |
||||
|
m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$$1], [$$2]))]]) |
||||
|
|
||||
|
# m4_shiftn(N, ...) |
||||
|
# ----------------- |
||||
|
# Returns ... shifted N times. Useful for recursive "varargs" constructs. |
||||
|
# |
||||
|
# m4_shiftn already validated arguments; we only need to speed up |
||||
|
# _m4_shiftn. If N is 3, then we build the temporary _m4_s, defined as |
||||
|
# ,[$5],[$6],...,[$m]_m4_popdef([_m4_s]) |
||||
|
# before calling m4_shift(_m4_s($@)). |
||||
|
m4_define([_m4_shiftn], |
||||
|
[m4_if(m4_incr([$1]), [$#], [], [m4_pushdef([_m4_s], |
||||
|
_m4_for(m4_eval([$1 + 2]), [$#], [1], |
||||
|
[[,]m4_dquote($], [)])[_m4_popdef([_m4_s])])m4_shift(_m4_s($@))])]) |
||||
|
|
||||
|
# m4_do(STRING, ...) |
||||
|
# ------------------ |
||||
|
# This macro invokes all its arguments (in sequence, of course). It is |
||||
|
# useful for making your macros more structured and readable by dropping |
||||
|
# unnecessary dnl's and have the macros indented properly. |
||||
|
# |
||||
|
# Here, we use the temporary macro _m4_do, defined as |
||||
|
# $1[]$2[]...[]$n[]_m4_popdef([_m4_do]) |
||||
|
m4_define([m4_do], |
||||
|
[m4_if([$#], [0], [], |
||||
|
[m4_pushdef([_$0], _m4_for([1], [$#], [1], |
||||
|
[$], [[[]]])[_m4_popdef([_$0])])_$0($@)])]) |
||||
|
|
||||
|
# m4_dquote_elt(ARGS) |
||||
|
# ------------------- |
||||
|
# Return ARGS as an unquoted list of double-quoted arguments. |
||||
|
# |
||||
|
# _m4_foreach to the rescue. |
||||
|
m4_define([m4_dquote_elt], |
||||
|
[m4_if([$#], [0], [], [[[$1]]_m4_foreach([,m4_dquote(], [)], $@)])]) |
||||
|
|
||||
|
# m4_reverse(ARGS) |
||||
|
# ---------------- |
||||
|
# Output ARGS in reverse order. |
||||
|
# |
||||
|
# Invoke _m4_r($@) with the temporary _m4_r built as |
||||
|
# [$m], [$m-1], ..., [$2], [$1]_m4_popdef([_m4_r]) |
||||
|
m4_define([m4_reverse], |
||||
|
[m4_if([$#], [0], [], [$#], [1], [[$1]], |
||||
|
[m4_pushdef([_m4_r], [[$$#]]_m4_for(m4_decr([$#]), [1], [-1], |
||||
|
[[, ]m4_dquote($], [)])[_m4_popdef([_m4_r])])_m4_r($@)])]) |
||||
|
|
||||
|
|
||||
|
# m4_map_args_pair(EXPRESSION, [END-EXPR = EXPRESSION], ARG...) |
||||
|
# ------------------------------------------------------------- |
||||
|
# Perform a pairwise grouping of consecutive ARGs, by expanding |
||||
|
# EXPRESSION([ARG1], [ARG2]). If there are an odd number of ARGs, the |
||||
|
# final argument is expanded with END-EXPR([ARGn]). |
||||
|
# |
||||
|
# Build the temporary macro _m4_map_args_pair, with the $2([$m+1]) |
||||
|
# only output if $# is odd: |
||||
|
# $1([$3], [$4])[]$1([$5], [$6])[]...$1([$m-1], |
||||
|
# [$m])[]m4_default([$2], [$1])([$m+1])[]_m4_popdef([_m4_map_args_pair]) |
||||
|
m4_define([m4_map_args_pair], |
||||
|
[m4_if([$#], [0], [m4_fatal([$0: too few arguments: $#])], |
||||
|
[$#], [1], [m4_fatal([$0: too few arguments: $#: $1])], |
||||
|
[$#], [2], [], |
||||
|
[$#], [3], [m4_default([$2], [$1])([$3])[]], |
||||
|
[m4_pushdef([_$0], _m4_for([3], |
||||
|
m4_eval([$# / 2 * 2 - 1]), [2], [_$0_(], [)])_$0_end( |
||||
|
[1], [2], [$#])[_m4_popdef([_$0])])_$0($@)])]) |
||||
|
|
||||
|
m4_define([_m4_map_args_pair_], |
||||
|
[$0_([1], [$1], m4_incr([$1]))]) |
||||
|
|
||||
|
m4_define([_m4_map_args_pair__], |
||||
|
[[$$1([$$2], [$$3])[]]]) |
||||
|
|
||||
|
m4_define([_m4_map_args_pair_end], |
||||
|
[m4_if(m4_eval([$3 & 1]), [1], [[m4_default([$$2], [$$1])([$$3])[]]])]) |
||||
|
|
||||
|
# m4_join(SEP, ARG1, ARG2...) |
||||
|
# --------------------------- |
||||
|
# Produce ARG1SEPARG2...SEPARGn. Avoid back-to-back SEP when a given ARG |
||||
|
# is the empty string. No expansion is performed on SEP or ARGs. |
||||
|
# |
||||
|
# Use a self-modifying separator, since we don't know how many |
||||
|
# arguments might be skipped before a separator is first printed, but |
||||
|
# be careful if the separator contains $. _m4_foreach to the rescue. |
||||
|
m4_define([m4_join], |
||||
|
[m4_pushdef([_m4_sep], [m4_define([_m4_sep], _m4_defn([m4_echo]))])]dnl |
||||
|
[_m4_foreach([_$0([$1],], [)], $@)_m4_popdef([_m4_sep])]) |
||||
|
|
||||
|
m4_define([_m4_join], |
||||
|
[m4_if([$2], [], [], [_m4_sep([$1])[$2]])]) |
||||
|
|
||||
|
# m4_joinall(SEP, ARG1, ARG2...) |
||||
|
# ------------------------------ |
||||
|
# Produce ARG1SEPARG2...SEPARGn. An empty ARG results in back-to-back SEP. |
||||
|
# No expansion is performed on SEP or ARGs. |
||||
|
# |
||||
|
# A bit easier than m4_join. _m4_foreach to the rescue. |
||||
|
m4_define([m4_joinall], |
||||
|
[[$2]m4_if(m4_eval([$# <= 2]), [1], [], |
||||
|
[_m4_foreach([$1], [], m4_shift($@))])]) |
||||
|
|
||||
|
# m4_list_cmp(A, B) |
||||
|
# ----------------- |
||||
|
# Compare the two lists of integer expressions A and B. |
||||
|
# |
||||
|
# m4_list_cmp takes care of any side effects; we only override |
||||
|
# _m4_list_cmp_raw, where we can safely expand lists multiple times. |
||||
|
# First, insert padding so that both lists are the same length; the |
||||
|
# trailing +0 is necessary to handle a missing list. Next, create a |
||||
|
# temporary macro to perform pairwise comparisons until an inequality |
||||
|
# is found. For example, m4_list_cmp([1], [1,2]) creates _m4_cmp as |
||||
|
# m4_if(m4_eval([($1) != ($3)]), [1], [m4_cmp([$1], [$3])], |
||||
|
# m4_eval([($2) != ($4)]), [1], [m4_cmp([$2], [$4])], |
||||
|
# [0]_m4_popdef([_m4_cmp])) |
||||
|
# then calls _m4_cmp([1+0], [0*2], [1], [2+0]) |
||||
|
m4_define([_m4_list_cmp_raw], |
||||
|
[m4_if([$1], [$2], 0, |
||||
|
[_m4_list_cmp($1+0_m4_list_pad(m4_count($1), m4_count($2)), |
||||
|
$2+0_m4_list_pad(m4_count($2), m4_count($1)))])]) |
||||
|
|
||||
|
m4_define([_m4_list_pad], |
||||
|
[m4_if(m4_eval($1 < $2), [1], |
||||
|
[_m4_for(m4_incr([$1]), [$2], [1], [,0*])])]) |
||||
|
|
||||
|
m4_define([_m4_list_cmp], |
||||
|
[m4_pushdef([_m4_cmp], [m4_if(]_m4_for( |
||||
|
[1], m4_eval([$# >> 1]), [1], [$0_(], [,]m4_eval([$# >> 1])[)])[ |
||||
|
[0]_m4_popdef([_m4_cmp]))])_m4_cmp($@)]) |
||||
|
|
||||
|
m4_define([_m4_list_cmp_], |
||||
|
[$0_([$1], m4_eval([$1 + $2]))]) |
||||
|
|
||||
|
m4_define([_m4_list_cmp__], |
||||
|
[[m4_eval([($$1) != ($$2)]), [1], [m4_cmp([$$1], [$$2])], |
||||
|
]]) |
||||
|
|
||||
|
# m4_max(EXPR, ...) |
||||
|
# m4_min(EXPR, ...) |
||||
|
# ----------------- |
||||
|
# Return the decimal value of the maximum (or minimum) in a series of |
||||
|
# integer expressions. |
||||
|
# |
||||
|
# _m4_foreach to the rescue; we only need to replace _m4_minmax. Here, |
||||
|
# we need a temporary macro to track the best answer so far, so that |
||||
|
# the foreach expression is tractable. |
||||
|
m4_define([_m4_minmax], |
||||
|
[m4_pushdef([_m4_best], m4_eval([$2]))_m4_foreach( |
||||
|
[m4_define([_m4_best], $1(_m4_best,], [))], m4_shift($@))]dnl |
||||
|
[_m4_best[]_m4_popdef([_m4_best])]) |
||||
|
|
||||
|
# m4_set_add_all(SET, VALUE...) |
||||
|
# ----------------------------- |
||||
|
# Add each VALUE into SET. This is O(n) in the number of VALUEs, and |
||||
|
# can be faster than calling m4_set_add for each VALUE. |
||||
|
# |
||||
|
# _m4_foreach to the rescue. If no deletions have occurred, then |
||||
|
# avoid the speed penalty of m4_set_add. |
||||
|
m4_define([m4_set_add_all], |
||||
|
[m4_if([$#], [0], [], [$#], [1], [], |
||||
|
[m4_define([_m4_set_size($1)], m4_eval(m4_set_size([$1]) |
||||
|
+ m4_len(_m4_foreach(m4_ifdef([_m4_set_cleanup($1)], |
||||
|
[[m4_set_add]], [[_$0]])[([$1],], [)], $@))))])]) |
||||
|
|
||||
|
m4_define([_m4_set_add_all], |
||||
|
[m4_ifdef([_m4_set([$1],$2)], [], |
||||
|
[m4_define([_m4_set([$1],$2)], |
||||
|
[1])m4_pushdef([_m4_set([$1])], [$2])-])]) |
||||
3329
tools/winflexbison/data/m4sugar/m4sugar.m4
File diff suppressed because it is too large
View File
1241
tools/winflexbison/data/skeletons/bison.m4
File diff suppressed because it is too large
View File
@ -0,0 +1,27 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# C++ skeleton dispatching for Bison. |
||||
|
|
||||
|
# Copyright (C) 2006-2007, 2009-2015, 2018-2021 Free Software |
||||
|
# Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
b4_glr_if( [m4_define([b4_used_skeleton], [b4_skeletonsdir/[glr.cc]])]) |
||||
|
b4_nondeterministic_if([m4_define([b4_used_skeleton], [b4_skeletonsdir/[glr.cc]])]) |
||||
|
|
||||
|
m4_define_default([b4_used_skeleton], [b4_skeletonsdir/[lalr1.cc]]) |
||||
|
m4_define_default([b4_skeleton], ["b4_basename(b4_used_skeleton)"]) |
||||
|
|
||||
|
m4_include(b4_used_skeleton) |
||||
@ -0,0 +1,778 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# C++ skeleton for Bison |
||||
|
|
||||
|
# Copyright (C) 2002-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
# Sanity checks, before defaults installed by c.m4. |
||||
|
b4_percent_define_ifdef([[api.value.union.name]], |
||||
|
[b4_complain_at(b4_percent_define_get_loc([[api.value.union.name]]), |
||||
|
[named %union is invalid in C++])]) |
||||
|
|
||||
|
b4_percent_define_default([[api.symbol.prefix]], [[S_]]) |
||||
|
|
||||
|
m4_include(b4_skeletonsdir/[c.m4]) |
||||
|
|
||||
|
b4_percent_define_check_kind([api.namespace], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([api.parser.class], [code], [deprecated]) |
||||
|
|
||||
|
|
||||
|
## ----- ## |
||||
|
## C++. ## |
||||
|
## ----- ## |
||||
|
|
||||
|
# b4_comment(TEXT, [PREFIX]) |
||||
|
# -------------------------- |
||||
|
# Put TEXT in comment. Prefix all the output lines with PREFIX. |
||||
|
m4_define([b4_comment], |
||||
|
[_b4_comment([$1], [$2// ], [$2// ])]) |
||||
|
|
||||
|
|
||||
|
# b4_inline(hh|cc) |
||||
|
# ---------------- |
||||
|
# Expand to `inline\n ` if $1 is hh. |
||||
|
m4_define([b4_inline], |
||||
|
[m4_case([$1], |
||||
|
[cc], [], |
||||
|
[hh], [[inline |
||||
|
]], |
||||
|
[m4_fatal([$0: invalid argument: $1])])]) |
||||
|
|
||||
|
|
||||
|
# b4_cxx_portability |
||||
|
# ------------------ |
||||
|
m4_define([b4_cxx_portability], |
||||
|
[#if defined __cplusplus |
||||
|
# define YY_CPLUSPLUS __cplusplus |
||||
|
#else |
||||
|
# define YY_CPLUSPLUS 199711L |
||||
|
#endif |
||||
|
|
||||
|
// Support move semantics when possible. |
||||
|
#if 201103L <= YY_CPLUSPLUS |
||||
|
# define YY_MOVE std::move |
||||
|
# define YY_MOVE_OR_COPY move |
||||
|
# define YY_MOVE_REF(Type) Type&& |
||||
|
# define YY_RVREF(Type) Type&& |
||||
|
# define YY_COPY(Type) Type |
||||
|
#else |
||||
|
# define YY_MOVE |
||||
|
# define YY_MOVE_OR_COPY copy |
||||
|
# define YY_MOVE_REF(Type) Type& |
||||
|
# define YY_RVREF(Type) const Type& |
||||
|
# define YY_COPY(Type) const Type& |
||||
|
#endif |
||||
|
|
||||
|
// Support noexcept when possible. |
||||
|
#if 201103L <= YY_CPLUSPLUS |
||||
|
# define YY_NOEXCEPT noexcept |
||||
|
# define YY_NOTHROW |
||||
|
#else |
||||
|
# define YY_NOEXCEPT |
||||
|
# define YY_NOTHROW throw () |
||||
|
#endif |
||||
|
|
||||
|
// Support constexpr when possible. |
||||
|
#if 201703 <= YY_CPLUSPLUS |
||||
|
# define YY_CONSTEXPR constexpr |
||||
|
#else |
||||
|
# define YY_CONSTEXPR |
||||
|
#endif[]dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
## ---------------- ## |
||||
|
## Default values. ## |
||||
|
## ---------------- ## |
||||
|
|
||||
|
b4_percent_define_default([[api.parser.class]], [[parser]]) |
||||
|
|
||||
|
# Don't do that so that we remember whether we're using a user |
||||
|
# request, or the default value. |
||||
|
# |
||||
|
# b4_percent_define_default([[api.location.type]], [[location]]) |
||||
|
|
||||
|
b4_percent_define_default([[api.filename.type]], [[const std::string]]) |
||||
|
# Make it a warning for those who used betas of Bison 3.0. |
||||
|
b4_percent_define_default([[api.namespace]], m4_defn([b4_prefix])) |
||||
|
|
||||
|
b4_percent_define_default([[define_location_comparison]], |
||||
|
[m4_if(b4_percent_define_get([[filename_type]]), |
||||
|
[std::string], [[true]], [[false]])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## ----------- ## |
||||
|
## Namespace. ## |
||||
|
## ----------- ## |
||||
|
|
||||
|
m4_define([b4_namespace_ref], [b4_percent_define_get([[api.namespace]])]) |
||||
|
|
||||
|
|
||||
|
# Don't permit an empty b4_namespace_ref. Any '::parser::foo' appended to it |
||||
|
# would compile as an absolute reference with 'parser' in the global namespace. |
||||
|
# b4_namespace_open would open an anonymous namespace and thus establish |
||||
|
# internal linkage. This would compile. However, it's cryptic, and internal |
||||
|
# linkage for the parser would be specified in all translation units that |
||||
|
# include the header, which is always generated. If we ever need to permit |
||||
|
# internal linkage somehow, surely we can find a cleaner approach. |
||||
|
m4_if(m4_bregexp(b4_namespace_ref, [^[ ]*$]), [-1], [], |
||||
|
[b4_complain_at(b4_percent_define_get_loc([[api.namespace]]), |
||||
|
[[namespace reference is empty]])]) |
||||
|
|
||||
|
# Instead of assuming the C++ compiler will do it, Bison should reject any |
||||
|
# invalid b4_namespace_ref that would be converted to a valid |
||||
|
# b4_namespace_open. The problem is that Bison doesn't always output |
||||
|
# b4_namespace_ref to uncommented code but should reserve the ability to do so |
||||
|
# in future releases without risking breaking any existing user grammars. |
||||
|
# Specifically, don't allow empty names as b4_namespace_open would just convert |
||||
|
# those into anonymous namespaces, and that might tempt some users. |
||||
|
m4_if(m4_bregexp(b4_namespace_ref, [::[ ]*::]), [-1], [], |
||||
|
[b4_complain_at(b4_percent_define_get_loc([[api.namespace]]), |
||||
|
[[namespace reference has consecutive "::"]])]) |
||||
|
m4_if(m4_bregexp(b4_namespace_ref, [::[ ]*$]), [-1], [], |
||||
|
[b4_complain_at(b4_percent_define_get_loc([[api.namespace]]), |
||||
|
[[namespace reference has a trailing "::"]])]) |
||||
|
|
||||
|
m4_define([b4_namespace_open], |
||||
|
[b4_user_code([b4_percent_define_get_syncline([[api.namespace]])dnl |
||||
|
[namespace ]m4_bpatsubst(m4_dquote(m4_bpatsubst(m4_dquote(b4_namespace_ref), |
||||
|
[^\(.\)[ ]*::], [\1])), |
||||
|
[::], [ { namespace ])[ {]])]) |
||||
|
|
||||
|
m4_define([b4_namespace_close], |
||||
|
[b4_user_code([b4_percent_define_get_syncline([[api.namespace]])dnl |
||||
|
m4_bpatsubst(m4_dquote(m4_bpatsubst(m4_dquote(b4_namespace_ref[ ]), |
||||
|
[^\(.\)[ ]*\(::\)?\([^][:]\|:[^:]\)*], |
||||
|
[\1])), |
||||
|
[::\([^][:]\|:[^:]\)*], [} ])[} // ]b4_namespace_ref])]) |
||||
|
|
||||
|
|
||||
|
## ------------- ## |
||||
|
## Token kinds. ## |
||||
|
## ------------- ## |
||||
|
|
||||
|
|
||||
|
# b4_token_enums |
||||
|
# -------------- |
||||
|
# Output the definition of the token kinds. |
||||
|
m4_define([b4_token_enums], |
||||
|
[[enum token_kind_type |
||||
|
{ |
||||
|
]b4_symbol([-2], [id])[ = -2, |
||||
|
]b4_symbol_foreach([b4_token_enum])dnl |
||||
|
[ };]dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## -------------- ## |
||||
|
## Symbol kinds. ## |
||||
|
## -------------- ## |
||||
|
|
||||
|
# b4_declare_symbol_enum |
||||
|
# ---------------------- |
||||
|
# The definition of the symbol internal numbers as an enum. |
||||
|
# Defining YYEMPTY here is important: it forces the compiler |
||||
|
# to use a signed type, which matters for yytoken. |
||||
|
m4_define([b4_declare_symbol_enum], |
||||
|
[[enum symbol_kind_type |
||||
|
{ |
||||
|
YYNTOKENS = ]b4_tokens_number[, ///< Number of tokens. |
||||
|
]b4_symbol(empty, kind_base)[ = -2, |
||||
|
]b4_symbol_foreach([ b4_symbol_enum])dnl |
||||
|
[ };]]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## ----------------- ## |
||||
|
## Semantic Values. ## |
||||
|
## ----------------- ## |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_value_type_declare |
||||
|
# --------------------- |
||||
|
# Declare value_type. |
||||
|
m4_define([b4_value_type_declare], |
||||
|
[b4_value_type_setup[]dnl |
||||
|
[ /// Symbol semantic values. |
||||
|
]m4_bmatch(b4_percent_define_get_kind([[api.value.type]]), |
||||
|
[code], |
||||
|
[[ typedef ]b4_percent_define_get([[api.value.type]])[ value_type;]], |
||||
|
[m4_bmatch(b4_percent_define_get([[api.value.type]]), |
||||
|
[union\|union-directive], |
||||
|
[[ union value_type |
||||
|
{ |
||||
|
]b4_user_union_members[ |
||||
|
};]])])dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_public_types_declare |
||||
|
# ----------------------- |
||||
|
# Define the public types: token, semantic value, location, and so forth. |
||||
|
# Depending on %define token_lex, may be output in the header or source file. |
||||
|
m4_define([b4_public_types_declare], |
||||
|
[b4_glr2_cc_if( |
||||
|
[b4_value_type_declare], |
||||
|
[[#ifdef ]b4_api_PREFIX[STYPE |
||||
|
# ifdef __GNUC__ |
||||
|
# pragma GCC message "bison: do not #define ]b4_api_PREFIX[STYPE in C++, use %define api.value.type" |
||||
|
# endif |
||||
|
typedef ]b4_api_PREFIX[STYPE value_type; |
||||
|
#else |
||||
|
]b4_value_type_declare[ |
||||
|
#endif |
||||
|
/// Backward compatibility (Bison 3.8). |
||||
|
typedef value_type semantic_type; |
||||
|
]])[]b4_locations_if([ |
||||
|
/// Symbol locations. |
||||
|
typedef b4_percent_define_get([[api.location.type]], |
||||
|
[[location]]) location_type;])[ |
||||
|
|
||||
|
/// Syntax errors thrown from user actions. |
||||
|
struct syntax_error : std::runtime_error |
||||
|
{ |
||||
|
syntax_error (]b4_locations_if([const location_type& l, ])[const std::string& m) |
||||
|
: std::runtime_error (m)]b4_locations_if([ |
||||
|
, location (l)])[ |
||||
|
{} |
||||
|
|
||||
|
syntax_error (const syntax_error& s) |
||||
|
: std::runtime_error (s.what ())]b4_locations_if([ |
||||
|
, location (s.location)])[ |
||||
|
{} |
||||
|
|
||||
|
~syntax_error () YY_NOEXCEPT YY_NOTHROW;]b4_locations_if([ |
||||
|
|
||||
|
location_type location;])[ |
||||
|
}; |
||||
|
|
||||
|
/// Token kinds. |
||||
|
struct token |
||||
|
{ |
||||
|
]b4_token_enums[]b4_glr2_cc_if([], [[ |
||||
|
/// Backward compatibility alias (Bison 3.6). |
||||
|
typedef token_kind_type yytokentype;]])[ |
||||
|
}; |
||||
|
|
||||
|
/// Token kind, as returned by yylex. |
||||
|
typedef token::token_kind_type token_kind_type;]b4_glr2_cc_if([], [[ |
||||
|
|
||||
|
/// Backward compatibility alias (Bison 3.6). |
||||
|
typedef token_kind_type token_type;]])[ |
||||
|
|
||||
|
/// Symbol kinds. |
||||
|
struct symbol_kind |
||||
|
{ |
||||
|
]b4_declare_symbol_enum[ |
||||
|
}; |
||||
|
|
||||
|
/// (Internal) symbol kind. |
||||
|
typedef symbol_kind::symbol_kind_type symbol_kind_type; |
||||
|
|
||||
|
/// The number of tokens. |
||||
|
static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS; |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_type_define |
||||
|
# --------------------- |
||||
|
# Define symbol_type, the external type for symbols used for symbol |
||||
|
# constructors. |
||||
|
m4_define([b4_symbol_type_define], |
||||
|
[[ /// A complete symbol. |
||||
|
/// |
||||
|
/// Expects its Base type to provide access to the symbol kind |
||||
|
/// via kind (). |
||||
|
/// |
||||
|
/// Provide access to semantic value]b4_locations_if([ and location])[. |
||||
|
template <typename Base> |
||||
|
struct basic_symbol : Base |
||||
|
{ |
||||
|
/// Alias to Base. |
||||
|
typedef Base super_type; |
||||
|
|
||||
|
/// Default constructor. |
||||
|
basic_symbol () YY_NOEXCEPT |
||||
|
: value ()]b4_locations_if([ |
||||
|
, location ()])[ |
||||
|
{} |
||||
|
|
||||
|
#if 201103L <= YY_CPLUSPLUS |
||||
|
/// Move constructor. |
||||
|
basic_symbol (basic_symbol&& that) |
||||
|
: Base (std::move (that)) |
||||
|
, value (]b4_variant_if([], [std::move (that.value)]))b4_locations_if([ |
||||
|
, location (std::move (that.location))])[ |
||||
|
{]b4_variant_if([ |
||||
|
b4_symbol_variant([this->kind ()], [value], [move], |
||||
|
[std::move (that.value)]) |
||||
|
])[} |
||||
|
#endif |
||||
|
|
||||
|
/// Copy constructor. |
||||
|
basic_symbol (const basic_symbol& that);]b4_variant_if([[ |
||||
|
|
||||
|
/// Constructors for typed symbols. |
||||
|
]b4_type_foreach([b4_basic_symbol_constructor_define], [ |
||||
|
])], [[ |
||||
|
/// Constructor for valueless symbols. |
||||
|
basic_symbol (typename Base::kind_type t]b4_locations_if([, |
||||
|
YY_MOVE_REF (location_type) l])[); |
||||
|
|
||||
|
/// Constructor for symbols with semantic value. |
||||
|
basic_symbol (typename Base::kind_type t, |
||||
|
YY_RVREF (value_type) v]b4_locations_if([, |
||||
|
YY_RVREF (location_type) l])[); |
||||
|
]])[ |
||||
|
/// Destroy the symbol. |
||||
|
~basic_symbol () |
||||
|
{ |
||||
|
clear (); |
||||
|
} |
||||
|
|
||||
|
]b4_glr2_cc_if([[ |
||||
|
/// Copy assignment. |
||||
|
basic_symbol& operator= (const basic_symbol& that) |
||||
|
{ |
||||
|
Base::operator= (that);]b4_variant_if([[ |
||||
|
]b4_symbol_variant([this->kind ()], [value], [copy], |
||||
|
[that.value])], [[ |
||||
|
value = that.value]])[;]b4_locations_if([[ |
||||
|
location = that.location;]])[ |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
/// Move assignment. |
||||
|
basic_symbol& operator= (basic_symbol&& that) |
||||
|
{ |
||||
|
Base::operator= (std::move (that));]b4_variant_if([[ |
||||
|
]b4_symbol_variant([this->kind ()], [value], [move], |
||||
|
[std::move (that.value)])], [[ |
||||
|
value = std::move (that.value)]])[;]b4_locations_if([[ |
||||
|
location = std::move (that.location);]])[ |
||||
|
return *this; |
||||
|
} |
||||
|
]])[ |
||||
|
|
||||
|
/// Destroy contents, and record that is empty. |
||||
|
void clear () YY_NOEXCEPT |
||||
|
{]b4_variant_if([[ |
||||
|
// User destructor. |
||||
|
symbol_kind_type yykind = this->kind (); |
||||
|
basic_symbol<Base>& yysym = *this; |
||||
|
(void) yysym; |
||||
|
switch (yykind) |
||||
|
{ |
||||
|
]b4_symbol_foreach([b4_symbol_destructor])dnl |
||||
|
[ default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// Value type destructor. |
||||
|
]b4_symbol_variant([[yykind]], [[value]], [[template destroy]])])[ |
||||
|
Base::clear (); |
||||
|
} |
||||
|
|
||||
|
]b4_parse_error_bmatch( |
||||
|
[custom\|detailed], |
||||
|
[[ /// The user-facing name of this symbol. |
||||
|
const char *name () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return ]b4_parser_class[::symbol_name (this->kind ()); |
||||
|
}]], |
||||
|
[simple], |
||||
|
[[#if ]b4_api_PREFIX[DEBUG || ]b4_token_table_flag[ |
||||
|
/// The user-facing name of this symbol. |
||||
|
const char *name () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return ]b4_parser_class[::symbol_name (this->kind ()); |
||||
|
} |
||||
|
#endif // #if ]b4_api_PREFIX[DEBUG || ]b4_token_table_flag[ |
||||
|
]], |
||||
|
[verbose], |
||||
|
[[ /// The user-facing name of this symbol. |
||||
|
std::string name () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return ]b4_parser_class[::symbol_name (this->kind ()); |
||||
|
}]])[]b4_glr2_cc_if([], [[ |
||||
|
|
||||
|
/// Backward compatibility (Bison 3.6). |
||||
|
symbol_kind_type type_get () const YY_NOEXCEPT;]])[ |
||||
|
|
||||
|
/// Whether empty. |
||||
|
bool empty () const YY_NOEXCEPT; |
||||
|
|
||||
|
/// Destructive move, \a s is emptied into this. |
||||
|
void move (basic_symbol& s); |
||||
|
|
||||
|
/// The semantic value. |
||||
|
value_type value;]b4_locations_if([ |
||||
|
|
||||
|
/// The location. |
||||
|
location_type location;])[ |
||||
|
|
||||
|
private: |
||||
|
#if YY_CPLUSPLUS < 201103L |
||||
|
/// Assignment operator. |
||||
|
basic_symbol& operator= (const basic_symbol& that); |
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
/// Type access provider for token (enum) based symbols. |
||||
|
struct by_kind |
||||
|
{ |
||||
|
/// The symbol kind as needed by the constructor. |
||||
|
typedef token_kind_type kind_type; |
||||
|
|
||||
|
/// Default constructor. |
||||
|
by_kind () YY_NOEXCEPT; |
||||
|
|
||||
|
#if 201103L <= YY_CPLUSPLUS |
||||
|
/// Move constructor. |
||||
|
by_kind (by_kind&& that) YY_NOEXCEPT; |
||||
|
#endif |
||||
|
|
||||
|
/// Copy constructor. |
||||
|
by_kind (const by_kind& that) YY_NOEXCEPT; |
||||
|
|
||||
|
/// Constructor from (external) token numbers. |
||||
|
by_kind (kind_type t) YY_NOEXCEPT; |
||||
|
|
||||
|
]b4_glr2_cc_if([[ |
||||
|
/// Copy assignment. |
||||
|
by_kind& operator= (const by_kind& that); |
||||
|
|
||||
|
/// Move assignment. |
||||
|
by_kind& operator= (by_kind&& that); |
||||
|
]])[ |
||||
|
|
||||
|
/// Record that this symbol is empty. |
||||
|
void clear () YY_NOEXCEPT; |
||||
|
|
||||
|
/// Steal the symbol kind from \a that. |
||||
|
void move (by_kind& that); |
||||
|
|
||||
|
/// The (internal) type number (corresponding to \a type). |
||||
|
/// \a empty when empty. |
||||
|
symbol_kind_type kind () const YY_NOEXCEPT;]b4_glr2_cc_if([], [[ |
||||
|
|
||||
|
/// Backward compatibility (Bison 3.6). |
||||
|
symbol_kind_type type_get () const YY_NOEXCEPT;]])[ |
||||
|
|
||||
|
/// The symbol kind. |
||||
|
/// \a ]b4_symbol_prefix[YYEMPTY when empty. |
||||
|
symbol_kind_type kind_; |
||||
|
};]b4_glr2_cc_if([], [[ |
||||
|
|
||||
|
/// Backward compatibility for a private implementation detail (Bison 3.6). |
||||
|
typedef by_kind by_type;]])[ |
||||
|
|
||||
|
/// "External" symbols: returned by the scanner. |
||||
|
struct symbol_type : basic_symbol<by_kind> |
||||
|
{]b4_variant_if([[ |
||||
|
/// Superclass. |
||||
|
typedef basic_symbol<by_kind> super_type; |
||||
|
|
||||
|
/// Empty symbol. |
||||
|
symbol_type () YY_NOEXCEPT {} |
||||
|
|
||||
|
/// Constructor for valueless symbols, and symbols from each type. |
||||
|
]b4_type_foreach([_b4_symbol_constructor_define])dnl |
||||
|
])[}; |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_public_types_define(hh|cc) |
||||
|
# ----------------------------- |
||||
|
# Provide the implementation needed by the public types. |
||||
|
m4_define([b4_public_types_define], |
||||
|
[[ // basic_symbol. |
||||
|
template <typename Base> |
||||
|
]b4_parser_class[::basic_symbol<Base>::basic_symbol (const basic_symbol& that) |
||||
|
: Base (that) |
||||
|
, value (]b4_variant_if([], [that.value]))b4_locations_if([ |
||||
|
, location (that.location)])[ |
||||
|
{]b4_variant_if([ |
||||
|
b4_symbol_variant([this->kind ()], [value], [copy], |
||||
|
[YY_MOVE (that.value)]) |
||||
|
])[} |
||||
|
|
||||
|
]b4_variant_if([], [[ |
||||
|
/// Constructor for valueless symbols. |
||||
|
template <typename Base> |
||||
|
]b4_parser_class[::basic_symbol<Base>::basic_symbol (]b4_join( |
||||
|
[typename Base::kind_type t], |
||||
|
b4_locations_if([YY_MOVE_REF (location_type) l]))[) |
||||
|
: Base (t) |
||||
|
, value ()]b4_locations_if([ |
||||
|
, location (l)])[ |
||||
|
{} |
||||
|
|
||||
|
template <typename Base> |
||||
|
]b4_parser_class[::basic_symbol<Base>::basic_symbol (]b4_join( |
||||
|
[typename Base::kind_type t], |
||||
|
[YY_RVREF (value_type) v], |
||||
|
b4_locations_if([YY_RVREF (location_type) l]))[) |
||||
|
: Base (t) |
||||
|
, value (]b4_variant_if([], [YY_MOVE (v)])[)]b4_locations_if([ |
||||
|
, location (YY_MOVE (l))])[ |
||||
|
{]b4_variant_if([[ |
||||
|
(void) v; |
||||
|
]b4_symbol_variant([this->kind ()], [value], [YY_MOVE_OR_COPY], [YY_MOVE (v)])])[}]])[ |
||||
|
|
||||
|
]b4_glr2_cc_if([], [[ |
||||
|
template <typename Base> |
||||
|
]b4_parser_class[::symbol_kind_type |
||||
|
]b4_parser_class[::basic_symbol<Base>::type_get () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return this->kind (); |
||||
|
} |
||||
|
]])[ |
||||
|
|
||||
|
template <typename Base> |
||||
|
bool |
||||
|
]b4_parser_class[::basic_symbol<Base>::empty () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return this->kind () == ]b4_symbol(empty, kind)[; |
||||
|
} |
||||
|
|
||||
|
template <typename Base> |
||||
|
void |
||||
|
]b4_parser_class[::basic_symbol<Base>::move (basic_symbol& s) |
||||
|
{ |
||||
|
super_type::move (s); |
||||
|
]b4_variant_if([b4_symbol_variant([this->kind ()], [value], [move], |
||||
|
[YY_MOVE (s.value)])], |
||||
|
[value = YY_MOVE (s.value);])[]b4_locations_if([ |
||||
|
location = YY_MOVE (s.location);])[ |
||||
|
} |
||||
|
|
||||
|
// by_kind. |
||||
|
]b4_inline([$1])b4_parser_class[::by_kind::by_kind () YY_NOEXCEPT |
||||
|
: kind_ (]b4_symbol(empty, kind)[) |
||||
|
{} |
||||
|
|
||||
|
#if 201103L <= YY_CPLUSPLUS |
||||
|
]b4_inline([$1])b4_parser_class[::by_kind::by_kind (by_kind&& that) YY_NOEXCEPT |
||||
|
: kind_ (that.kind_) |
||||
|
{ |
||||
|
that.clear (); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
]b4_inline([$1])b4_parser_class[::by_kind::by_kind (const by_kind& that) YY_NOEXCEPT |
||||
|
: kind_ (that.kind_) |
||||
|
{} |
||||
|
|
||||
|
]b4_inline([$1])b4_parser_class[::by_kind::by_kind (token_kind_type t) YY_NOEXCEPT |
||||
|
: kind_ (yytranslate_ (t)) |
||||
|
{} |
||||
|
|
||||
|
]b4_glr2_cc_if([[ |
||||
|
]b4_inline([$1])]b4_parser_class[::by_kind& |
||||
|
b4_parser_class[::by_kind::by_kind::operator= (const by_kind& that) |
||||
|
{ |
||||
|
kind_ = that.kind_; |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
]b4_inline([$1])]b4_parser_class[::by_kind& |
||||
|
b4_parser_class[::by_kind::by_kind::operator= (by_kind&& that) |
||||
|
{ |
||||
|
kind_ = that.kind_; |
||||
|
that.clear (); |
||||
|
return *this; |
||||
|
} |
||||
|
]])[ |
||||
|
|
||||
|
]b4_inline([$1])[void |
||||
|
]b4_parser_class[::by_kind::clear () YY_NOEXCEPT |
||||
|
{ |
||||
|
kind_ = ]b4_symbol(empty, kind)[; |
||||
|
} |
||||
|
|
||||
|
]b4_inline([$1])[void |
||||
|
]b4_parser_class[::by_kind::move (by_kind& that) |
||||
|
{ |
||||
|
kind_ = that.kind_; |
||||
|
that.clear (); |
||||
|
} |
||||
|
|
||||
|
]b4_inline([$1])[]b4_parser_class[::symbol_kind_type |
||||
|
]b4_parser_class[::by_kind::kind () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return kind_; |
||||
|
} |
||||
|
|
||||
|
]b4_glr2_cc_if([], [[ |
||||
|
]b4_inline([$1])[]b4_parser_class[::symbol_kind_type |
||||
|
]b4_parser_class[::by_kind::type_get () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return this->kind (); |
||||
|
} |
||||
|
]])[ |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_token_constructor_define |
||||
|
# ---------------------------- |
||||
|
# Define make_FOO for all the token kinds. |
||||
|
# Use at class-level. Redefined in variant.hh. |
||||
|
m4_define([b4_token_constructor_define], []) |
||||
|
|
||||
|
|
||||
|
# b4_yytranslate_define(cc|hh) |
||||
|
# ---------------------------- |
||||
|
# Define yytranslate_. Sometimes used in the header file ($1=hh), |
||||
|
# sometimes in the cc file. |
||||
|
m4_define([b4_yytranslate_define], |
||||
|
[ b4_inline([$1])b4_parser_class[::symbol_kind_type |
||||
|
]b4_parser_class[::yytranslate_ (int t) YY_NOEXCEPT |
||||
|
{ |
||||
|
]b4_api_token_raw_if( |
||||
|
[[ return static_cast<symbol_kind_type> (t);]], |
||||
|
[[ // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to |
||||
|
// TOKEN-NUM as returned by yylex. |
||||
|
static |
||||
|
const ]b4_int_type_for([b4_translate])[ |
||||
|
translate_table[] = |
||||
|
{ |
||||
|
]b4_translate[ |
||||
|
}; |
||||
|
// Last valid token kind. |
||||
|
const int code_max = ]b4_code_max[; |
||||
|
|
||||
|
if (t <= 0) |
||||
|
return symbol_kind::]b4_symbol_prefix[YYEOF; |
||||
|
else if (t <= code_max) |
||||
|
return static_cast <symbol_kind_type> (translate_table[t]); |
||||
|
else |
||||
|
return symbol_kind::]b4_symbol_prefix[YYUNDEF;]])[ |
||||
|
} |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_lhs_value([TYPE]) |
||||
|
# -------------------- |
||||
|
m4_define([b4_lhs_value], |
||||
|
[b4_symbol_value([yyval], [$1])]) |
||||
|
|
||||
|
|
||||
|
# b4_rhs_value(RULE-LENGTH, POS, [TYPE]) |
||||
|
# -------------------------------------- |
||||
|
# FIXME: Dead code. |
||||
|
m4_define([b4_rhs_value], |
||||
|
[b4_symbol_value([yysemantic_stack_@{($1) - ($2)@}], [$3])]) |
||||
|
|
||||
|
|
||||
|
# b4_lhs_location() |
||||
|
# ----------------- |
||||
|
# Expansion of @$. |
||||
|
m4_define([b4_lhs_location], |
||||
|
[(yyloc)]) |
||||
|
|
||||
|
|
||||
|
# b4_rhs_location(RULE-LENGTH, POS) |
||||
|
# --------------------------------- |
||||
|
# Expansion of @POS, where the current rule has RULE-LENGTH symbols |
||||
|
# on RHS. |
||||
|
m4_define([b4_rhs_location], |
||||
|
[(yylocation_stack_@{($1) - ($2)@})]) |
||||
|
|
||||
|
|
||||
|
# b4_parse_param_decl |
||||
|
# ------------------- |
||||
|
# Extra formal arguments of the constructor. |
||||
|
# Change the parameter names from "foo" into "foo_yyarg", so that |
||||
|
# there is no collision bw the user chosen attribute name, and the |
||||
|
# argument name in the constructor. |
||||
|
m4_define([b4_parse_param_decl], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[m4_map_sep([b4_parse_param_decl_1], [, ], [b4_parse_param])])]) |
||||
|
|
||||
|
m4_define([b4_parse_param_decl_1], |
||||
|
[$1_yyarg]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_cons |
||||
|
# ------------------- |
||||
|
# Extra initialisations of the constructor. |
||||
|
m4_define([b4_parse_param_cons], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[ |
||||
|
b4_cc_constructor_calls(b4_parse_param)])]) |
||||
|
m4_define([b4_cc_constructor_calls], |
||||
|
[m4_map_sep([b4_cc_constructor_call], [, |
||||
|
], [$@])]) |
||||
|
m4_define([b4_cc_constructor_call], |
||||
|
[$2 ($2_yyarg)]) |
||||
|
|
||||
|
# b4_parse_param_vars |
||||
|
# ------------------- |
||||
|
# Extra instance variables. |
||||
|
m4_define([b4_parse_param_vars], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[ |
||||
|
// User arguments. |
||||
|
b4_cc_var_decls(b4_parse_param)])]) |
||||
|
m4_define([b4_cc_var_decls], |
||||
|
[m4_map_sep([b4_cc_var_decl], [ |
||||
|
], [$@])]) |
||||
|
m4_define([b4_cc_var_decl], |
||||
|
[ $1;]) |
||||
|
|
||||
|
|
||||
|
## ---------## |
||||
|
## Values. ## |
||||
|
## ---------## |
||||
|
|
||||
|
# b4_yylloc_default_define |
||||
|
# ------------------------ |
||||
|
# Define YYLLOC_DEFAULT. |
||||
|
m4_define([b4_yylloc_default_define], |
||||
|
[[/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. |
||||
|
If N is 0, then set CURRENT to the empty location which ends |
||||
|
the previous symbol: RHS[0] (always defined). */ |
||||
|
|
||||
|
# ifndef YYLLOC_DEFAULT |
||||
|
# define YYLLOC_DEFAULT(Current, Rhs, N) \ |
||||
|
do \ |
||||
|
if (N) \ |
||||
|
{ \ |
||||
|
(Current).begin = YYRHSLOC (Rhs, 1).begin; \ |
||||
|
(Current).end = YYRHSLOC (Rhs, N).end; \ |
||||
|
} \ |
||||
|
else \ |
||||
|
{ \ |
||||
|
(Current).begin = (Current).end = YYRHSLOC (Rhs, 0).end; \ |
||||
|
} \ |
||||
|
while (false) |
||||
|
# endif |
||||
|
]]) |
||||
|
|
||||
|
## -------- ## |
||||
|
## Checks. ## |
||||
|
## -------- ## |
||||
|
|
||||
|
b4_token_ctor_if([b4_variant_if([], |
||||
|
[b4_fatal_at(b4_percent_define_get_loc(api.token.constructor), |
||||
|
[cannot use '%s' without '%s'], |
||||
|
[%define api.token.constructor], |
||||
|
[%define api.value.type variant]))])]) |
||||
@ -0,0 +1,72 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# Common code for C-like languages (C, C++, Java, etc.) |
||||
|
|
||||
|
# Copyright (C) 2012-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
|
||||
|
# _b4_comment(TEXT, OPEN, CONTINUE, END) |
||||
|
# -------------------------------------- |
||||
|
# Put TEXT in comment. Avoid trailing spaces: don't indent empty lines. |
||||
|
# Avoid adding indentation to the first line, as the indentation comes |
||||
|
# from OPEN. That's why we don't patsubst([$1], [^\(.\)], [ \1]). |
||||
|
# Turn "*/" in TEXT into "* /" so that we don't unexpectedly close |
||||
|
# the comments before its end. |
||||
|
# |
||||
|
# Prefix all the output lines with PREFIX. |
||||
|
m4_define([_b4_comment], |
||||
|
[$2[]b4_gsub(m4_expand([$1]), |
||||
|
[[*]/], [*\\/], |
||||
|
[/[*]], [/\\*], |
||||
|
[ |
||||
|
\(.\)], [ |
||||
|
$3\1])$4]) |
||||
|
|
||||
|
|
||||
|
# b4_comment(TEXT, [PREFIX]) |
||||
|
# -------------------------- |
||||
|
# Put TEXT in comment. Prefix all the output lines with PREFIX. |
||||
|
m4_define([b4_comment], |
||||
|
[_b4_comment([$1], [$2/* ], [$2 ], [ */])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
# _b4_dollar_dollar(VALUE, SYMBOL-NUM, FIELD, DEFAULT-FIELD) |
||||
|
# ---------------------------------------------------------- |
||||
|
# If FIELD (or DEFAULT-FIELD) is non-null, return "VALUE.FIELD", |
||||
|
# otherwise just VALUE. Be sure to pass "(VALUE)" if VALUE is a |
||||
|
# pointer. |
||||
|
m4_define([_b4_dollar_dollar], |
||||
|
[b4_symbol_value([$1], |
||||
|
[$2], |
||||
|
m4_if([$3], [[]], |
||||
|
[[$4]], [[$3]]))]) |
||||
|
|
||||
|
# b4_dollar_pushdef(VALUE-POINTER, SYMBOL-NUM, [TYPE_TAG], LOCATION) |
||||
|
# b4_dollar_popdef |
||||
|
# ------------------------------------------------------------------ |
||||
|
# Define b4_dollar_dollar for VALUE-POINTER and DEFAULT-FIELD, |
||||
|
# and b4_at_dollar for LOCATION. |
||||
|
m4_define([b4_dollar_pushdef], |
||||
|
[m4_pushdef([b4_dollar_dollar], |
||||
|
[_b4_dollar_dollar([$1], [$2], m4_dquote($][1), [$3])])dnl |
||||
|
m4_pushdef([b4_at_dollar], [$4])dnl |
||||
|
]) |
||||
|
m4_define([b4_dollar_popdef], |
||||
|
[m4_popdef([b4_at_dollar])dnl |
||||
|
m4_popdef([b4_dollar_dollar])dnl |
||||
|
]) |
||||
@ -0,0 +1,27 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# C skeleton dispatching for Bison. |
||||
|
|
||||
|
# Copyright (C) 2006-2007, 2009-2015, 2018-2021 Free Software |
||||
|
# Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
b4_glr_if( [m4_define([b4_used_skeleton], [b4_skeletonsdir/[glr.c]])]) |
||||
|
b4_nondeterministic_if([m4_define([b4_used_skeleton], [b4_skeletonsdir/[glr.c]])]) |
||||
|
|
||||
|
m4_define_default([b4_used_skeleton], [b4_skeletonsdir/[yacc.c]]) |
||||
|
m4_define_default([b4_skeleton], ["b4_basename(b4_used_skeleton)"]) |
||||
|
|
||||
|
m4_include(b4_used_skeleton) |
||||
1125
tools/winflexbison/data/skeletons/c.m4
File diff suppressed because it is too large
View File
@ -0,0 +1,26 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# D skeleton dispatching for Bison. |
||||
|
|
||||
|
# Copyright (C) 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
b4_glr_if( [b4_complain([%%glr-parser not supported for D])]) |
||||
|
b4_nondeterministic_if([b4_complain([%%nondeterministic-parser not supported for D])]) |
||||
|
|
||||
|
m4_define_default([b4_used_skeleton], [b4_skeletonsdir/[lalr1.d]]) |
||||
|
m4_define_default([b4_skeleton], ["b4_basename(b4_used_skeleton)"]) |
||||
|
|
||||
|
m4_include(b4_used_skeleton) |
||||
@ -0,0 +1,628 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# D language support for Bison |
||||
|
|
||||
|
# Copyright (C) 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
|
||||
|
m4_include(b4_skeletonsdir/[c-like.m4]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_action(SYMBOL-NUM, ACTION) |
||||
|
# ------------------------------------ |
||||
|
# Run the action ACTION ("destructor" or "printer") for SYMBOL-NUM. |
||||
|
m4_define([b4_symbol_action], |
||||
|
[b4_symbol_if([$1], [has_$2], |
||||
|
[b4_dollar_pushdef([yyval], |
||||
|
[$1], |
||||
|
[], |
||||
|
[yyloc])dnl |
||||
|
_b4_symbol_case([$1])[]dnl |
||||
|
b4_syncline([b4_symbol([$1], [$2_line])], [b4_symbol([$1], [$2_file])])dnl |
||||
|
b4_symbol([$1], [$2]) |
||||
|
b4_syncline([@oline@], [@ofile@])dnl |
||||
|
break; |
||||
|
|
||||
|
b4_dollar_popdef[]dnl |
||||
|
])]) |
||||
|
|
||||
|
|
||||
|
# b4_use(EXPR) |
||||
|
# ------------ |
||||
|
# Pacify the compiler about some maybe unused value. |
||||
|
m4_define([b4_use], |
||||
|
[]) |
||||
|
|
||||
|
|
||||
|
# b4_sync_start(LINE, FILE) |
||||
|
# ------------------------- |
||||
|
m4_define([b4_sync_start], [[#]line $1 $2]) |
||||
|
|
||||
|
|
||||
|
# b4_list2(LIST1, LIST2) |
||||
|
# ---------------------- |
||||
|
# Join two lists with a comma if necessary. |
||||
|
m4_define([b4_list2], |
||||
|
[$1[]m4_ifval(m4_quote($1), [m4_ifval(m4_quote($2), [[, ]])])[]$2]) |
||||
|
|
||||
|
|
||||
|
# b4_percent_define_get3(DEF, PRE, POST, NOT) |
||||
|
# ------------------------------------------- |
||||
|
# Expand to the value of DEF surrounded by PRE and POST if it's %define'ed, |
||||
|
# otherwise NOT. |
||||
|
m4_define([b4_percent_define_get3], |
||||
|
[m4_ifval(m4_quote(b4_percent_define_get([$1])), |
||||
|
[$2[]b4_percent_define_get([$1])[]$3], [$4])]) |
||||
|
|
||||
|
# b4_percent_define_if_get2(ARG1, ARG2, DEF, NOT) |
||||
|
# ----------------------------------------------- |
||||
|
# Expand to the value of DEF if ARG1 or ARG2 are %define'ed, |
||||
|
# otherwise NOT. |
||||
|
m4_define([b4_percent_define_if_get2], |
||||
|
[m4_ifval(m4_quote(b4_percent_define_get([$1])), |
||||
|
[$3], [m4_ifval(m4_quote(b4_percent_define_get([$2])), |
||||
|
[$3], [$4])])]) |
||||
|
|
||||
|
# b4_percent_define_class_before_interface(CLASS, INTERFACE) |
||||
|
# ---------------------------------------------------------- |
||||
|
# Expand to a ', ' if both a class and an interface have been %define'ed |
||||
|
m4_define([b4_percent_define_class_before_interface], |
||||
|
[m4_ifval(m4_quote(b4_percent_define_get([$1])), |
||||
|
[m4_ifval(m4_quote(b4_percent_define_get([$2])), |
||||
|
[, ])])]) |
||||
|
|
||||
|
|
||||
|
# b4_flag_value(BOOLEAN-FLAG) |
||||
|
# --------------------------- |
||||
|
m4_define([b4_flag_value], [b4_flag_if([$1], [true], [false])]) |
||||
|
|
||||
|
|
||||
|
# b4_parser_class_declaration |
||||
|
# --------------------------- |
||||
|
# The declaration of the parser class ("class YYParser"), with all its |
||||
|
# qualifiers/annotations. |
||||
|
b4_percent_define_default([[api.parser.abstract]], [[false]]) |
||||
|
b4_percent_define_default([[api.parser.final]], [[false]]) |
||||
|
b4_percent_define_default([[api.parser.public]], [[false]]) |
||||
|
|
||||
|
m4_define([b4_parser_class_declaration], |
||||
|
[b4_percent_define_get3([api.parser.annotations], [], [ ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.public], [public ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.abstract], [abstract ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.final], [final ])dnl |
||||
|
[class ]b4_parser_class[]dnl |
||||
|
b4_percent_define_if_get2([api.parser.extends], [api.parser.implements], [ : ])dnl |
||||
|
b4_percent_define_get([api.parser.extends])dnl |
||||
|
b4_percent_define_class_before_interface([api.parser.extends], [api.parser.implements])dnl |
||||
|
b4_percent_define_get([api.parser.implements])]) |
||||
|
|
||||
|
|
||||
|
# b4_lexer_if(TRUE, FALSE) |
||||
|
# ------------------------ |
||||
|
m4_define([b4_lexer_if], |
||||
|
[b4_percent_code_ifdef([[lexer]], [$1], [$2])]) |
||||
|
|
||||
|
|
||||
|
# b4_position_type_if(TRUE, FALSE) |
||||
|
# -------------------------------- |
||||
|
m4_define([b4_position_type_if], |
||||
|
[b4_percent_define_ifdef([[position_type]], [$1], [$2])]) |
||||
|
|
||||
|
|
||||
|
# b4_location_type_if(TRUE, FALSE) |
||||
|
# -------------------------------- |
||||
|
m4_define([b4_location_type_if], |
||||
|
[b4_percent_define_ifdef([[location_type]], [$1], [$2])]) |
||||
|
|
||||
|
|
||||
|
# b4_identification |
||||
|
# ----------------- |
||||
|
m4_define([b4_identification], |
||||
|
[[/** Version number for the Bison executable that generated this parser. */ |
||||
|
public static immutable string yy_bison_version = "]b4_version_string["; |
||||
|
|
||||
|
/** Name of the skeleton that generated this parser. */ |
||||
|
public static immutable string yy_bison_skeleton = ]b4_skeleton[; |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
## ------------ ## |
||||
|
## Data types. ## |
||||
|
## ------------ ## |
||||
|
|
||||
|
# b4_int_type(MIN, MAX) |
||||
|
# --------------------- |
||||
|
# Return the smallest int type able to handle numbers ranging from |
||||
|
# MIN to MAX (included). |
||||
|
m4_define([b4_int_type], |
||||
|
[m4_if(b4_ints_in($@, [-128], [127]), [1], [byte], |
||||
|
b4_ints_in($@, [-32768], [32767]), [1], [short], |
||||
|
[int])]) |
||||
|
|
||||
|
# b4_int_type_for(NAME) |
||||
|
# --------------------- |
||||
|
# Return the smallest int type able to handle numbers ranging from |
||||
|
# `NAME_min' to `NAME_max' (included). |
||||
|
m4_define([b4_int_type_for], |
||||
|
[b4_int_type($1_min, $1_max)]) |
||||
|
|
||||
|
# b4_null |
||||
|
# ------- |
||||
|
m4_define([b4_null], [null]) |
||||
|
|
||||
|
|
||||
|
# b4_integral_parser_table_define(NAME, DATA, COMMENT) |
||||
|
#----------------------------------------------------- |
||||
|
# Define "yy<TABLE-NAME>" whose contents is CONTENT. |
||||
|
m4_define([b4_integral_parser_table_define], |
||||
|
[m4_ifvaln([$3], [b4_comment([$3], [ ])])dnl |
||||
|
private static immutable b4_int_type_for([$2])[[]] yy$1_ = |
||||
|
@{ |
||||
|
$2 |
||||
|
@};dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
## ------------- ## |
||||
|
## Token kinds. ## |
||||
|
## ------------- ## |
||||
|
|
||||
|
m4_define([b4_symbol(-2, id)], [[YYEMPTY]]) |
||||
|
b4_percent_define_default([[api.token.raw]], [[true]]) |
||||
|
|
||||
|
# b4_token_enum(TOKEN-NAME, TOKEN-NUMBER) |
||||
|
# --------------------------------------- |
||||
|
# Output the definition of this token as an enum. |
||||
|
m4_define([b4_token_enum], |
||||
|
[b4_token_format([ %s = %s, |
||||
|
], [$1])]) |
||||
|
|
||||
|
# b4_token_enums |
||||
|
# -------------- |
||||
|
# Output the definition of the tokens as enums. |
||||
|
m4_define([b4_token_enums], |
||||
|
[/* Token kinds. */ |
||||
|
public enum TokenKind { |
||||
|
]b4_symbol(empty, id)[ = -2, |
||||
|
b4_symbol_foreach([b4_token_enum])dnl |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
# b4_symbol_translate(STRING) |
||||
|
# --------------------------- |
||||
|
# Used by "bison" in the array of symbol names to mark those that |
||||
|
# require translation. |
||||
|
m4_define([b4_symbol_translate], |
||||
|
[[_($1)]]) |
||||
|
|
||||
|
|
||||
|
# _b4_token_constructor_define(SYMBOL-NUM) |
||||
|
# ---------------------------------------- |
||||
|
# Define Symbol.FOO for SYMBOL-NUM. |
||||
|
m4_define([_b4_token_constructor_define], |
||||
|
[b4_token_visible_if([$1], |
||||
|
[[ |
||||
|
static auto ]b4_symbol([$1], [id])[(]b4_symbol_if([$1], [has_type], |
||||
|
[b4_union_if([b4_symbol([$1], [type]], |
||||
|
[[typeof(YYSemanticType.]b4_symbol([$1], [type])[]])) [val]])dnl |
||||
|
[]b4_locations_if([b4_symbol_if([$1], [has_type], [[, ]])[Location l]])[) |
||||
|
{ |
||||
|
return Symbol(TokenKind.]b4_symbol([$1], [id])[]b4_symbol_if([$1], [has_type], |
||||
|
[[, val]])[]b4_locations_if([[, l]])[); |
||||
|
}]])]) |
||||
|
|
||||
|
# b4_token_constructor_define |
||||
|
# --------------------------- |
||||
|
# Define Symbol.FOO for each token kind FOO. |
||||
|
m4_define([b4_token_constructor_define], |
||||
|
[[ |
||||
|
/* Implementation of token constructors for each symbol type visible to |
||||
|
* the user. The code generates static methods that have the same names |
||||
|
* as the TokenKinds. |
||||
|
*/]b4_symbol_foreach([_b4_token_constructor_define])dnl |
||||
|
]) |
||||
|
|
||||
|
## -------------- ## |
||||
|
## Symbol kinds. ## |
||||
|
## -------------- ## |
||||
|
|
||||
|
# b4_symbol_kind(NUM) |
||||
|
# ------------------- |
||||
|
m4_define([b4_symbol_kind], |
||||
|
[SymbolKind.b4_symbol_kind_base($@)]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_enum(SYMBOL-NUM) |
||||
|
# -------------------------- |
||||
|
# Output the definition of this symbol as an enum. |
||||
|
m4_define([b4_symbol_enum], |
||||
|
[m4_format([ %-30s %s], |
||||
|
m4_format([[%s = %s,]], |
||||
|
b4_symbol([$1], [kind_base]), |
||||
|
[$1]), |
||||
|
[b4_symbol_tag_comment([$1])])]) |
||||
|
|
||||
|
|
||||
|
# b4_declare_symbol_enum |
||||
|
# ---------------------- |
||||
|
# The definition of the symbol internal numbers as an enum. |
||||
|
# Defining YYEMPTY here is important: it forces the compiler |
||||
|
# to use a signed type, which matters for yytoken. |
||||
|
m4_define([b4_declare_symbol_enum], |
||||
|
[[ /* Symbol kinds. */ |
||||
|
struct SymbolKind |
||||
|
{ |
||||
|
enum |
||||
|
{ |
||||
|
]b4_symbol(empty, kind_base)[ = -2, /* No symbol. */ |
||||
|
]b4_symbol_foreach([b4_symbol_enum])dnl |
||||
|
[ } |
||||
|
|
||||
|
private int yycode_; |
||||
|
alias yycode_ this; |
||||
|
|
||||
|
this(int code) |
||||
|
{ |
||||
|
yycode_ = code; |
||||
|
} |
||||
|
|
||||
|
/* Return YYSTR after stripping away unnecessary quotes and |
||||
|
backslashes, so that it's suitable for yyerror. The heuristic is |
||||
|
that double-quoting is unnecessary unless the string contains an |
||||
|
apostrophe, a comma, or backslash (other than backslash-backslash). |
||||
|
YYSTR is taken from yytname. */ |
||||
|
final void toString(W)(W sink) const |
||||
|
if (isOutputRange!(W, char)) |
||||
|
{ |
||||
|
immutable string[] yy_sname = @{ |
||||
|
]b4_symbol_names[ |
||||
|
@};]b4_has_translations_if([[ |
||||
|
/* YYTRANSLATABLE[SYMBOL-NUM] -- Whether YY_SNAME[SYMBOL-NUM] is |
||||
|
internationalizable. */ |
||||
|
immutable ]b4_int_type_for([b4_translatable])[[] yytranslatable = @{ |
||||
|
]b4_translatable[ |
||||
|
@};]])[ |
||||
|
|
||||
|
put(sink, yy_sname[yycode_]); |
||||
|
} |
||||
|
} |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_case(ID, CODE, [COMMENTS]) |
||||
|
# ----------------------------- |
||||
|
m4_define([b4_case], [ case $1:m4_ifval([$3], [ b4_comment([$3])]) |
||||
|
$2 |
||||
|
break;]) |
||||
|
|
||||
|
|
||||
|
## ---------------- ## |
||||
|
## Default values. ## |
||||
|
## ---------------- ## |
||||
|
|
||||
|
m4_define([b4_yystype], [b4_percent_define_get([[stype]])]) |
||||
|
b4_percent_define_default([[stype]], [[YYSemanticType]])]) |
||||
|
|
||||
|
# %name-prefix |
||||
|
m4_define_default([b4_prefix], [[YY]]) |
||||
|
|
||||
|
b4_percent_define_default([[api.parser.class]], [b4_prefix[]Parser])]) |
||||
|
m4_define([b4_parser_class], [b4_percent_define_get([[api.parser.class]])]) |
||||
|
|
||||
|
#b4_percent_define_default([[location_type]], [Location])]) |
||||
|
m4_define([b4_location_type], b4_percent_define_ifdef([[location_type]],[b4_percent_define_get([[location_type]])],[YYLocation])) |
||||
|
|
||||
|
#b4_percent_define_default([[position_type]], [Position])]) |
||||
|
m4_define([b4_position_type], b4_percent_define_ifdef([[position_type]],[b4_percent_define_get([[position_type]])],[YYPosition])) |
||||
|
|
||||
|
|
||||
|
## ---------------- ## |
||||
|
## api.value.type. ## |
||||
|
## ---------------- ## |
||||
|
|
||||
|
|
||||
|
# ---------------------- # |
||||
|
# api.value.type=union. # |
||||
|
# ---------------------- # |
||||
|
|
||||
|
# b4_symbol_type_register(SYMBOL-NUM) |
||||
|
# ----------------------------------- |
||||
|
# Symbol SYMBOL-NUM has a type (for union) instead of a type-tag. |
||||
|
# Extend the definition of %union's body (b4_union_members) with a |
||||
|
# field of that type, and extend the symbol's "type" field to point to |
||||
|
# the field name, instead of the type name. |
||||
|
m4_define([b4_symbol_type_register], |
||||
|
[m4_define([b4_symbol($1, type_tag)], |
||||
|
[b4_symbol_if([$1], [has_id], |
||||
|
[b4_symbol([$1], [id])], |
||||
|
[yykind_[]b4_symbol([$1], [number])])])dnl |
||||
|
m4_append([b4_union_members], |
||||
|
m4_expand([m4_format([ %-40s %s], |
||||
|
m4_expand([b4_symbol([$1], [type]) b4_symbol([$1], [type_tag]);]), |
||||
|
[b4_symbol_tag_comment([$1])])])) |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_type_define_tag(SYMBOL1-NUM, ...) |
||||
|
# ------------------------------------ |
||||
|
# For the batch of symbols SYMBOL1-NUM... (which all have the same |
||||
|
# type), enhance the %union definition for each of them, and set |
||||
|
# there "type" field to the field tag name, instead of the type name. |
||||
|
m4_define([b4_type_define_tag], |
||||
|
[b4_symbol_if([$1], [has_type], |
||||
|
[m4_map([b4_symbol_type_register], [$@])]) |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_value_union(VAL, SYMBOL-NUM, [TYPE]) |
||||
|
# ---------------------------------------------- |
||||
|
# Same of b4_symbol_value, but when api.value.type=union. |
||||
|
m4_define([b4_symbol_value_union], |
||||
|
[m4_ifval([$3], |
||||
|
[(*($3*)(&$1))], |
||||
|
[m4_ifval([$2], |
||||
|
[b4_symbol_if([$2], [has_type], |
||||
|
[($1.b4_symbol([$2], [type_tag]))], |
||||
|
[$1])], |
||||
|
[$1])])]) |
||||
|
|
||||
|
|
||||
|
# b4_value_type_setup_union |
||||
|
# ------------------------- |
||||
|
# Setup support for api.value.type=union. Symbols are defined with a |
||||
|
# type instead of a union member name: build the corresponding union, |
||||
|
# and give the symbols their tag. |
||||
|
m4_define([b4_value_type_setup_union], |
||||
|
[m4_define([b4_union_members]) |
||||
|
b4_type_foreach([b4_type_define_tag]) |
||||
|
m4_copy_force([b4_symbol_value_union], [b4_symbol_value]) |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# _b4_value_type_setup_keyword |
||||
|
# ---------------------------- |
||||
|
# api.value.type is defined with a keyword/string syntax. Check if |
||||
|
# that is properly defined, and prepare its use. |
||||
|
m4_define([_b4_value_type_setup_keyword], |
||||
|
[b4_percent_define_check_values([[[[api.value.type]], |
||||
|
[[none]], |
||||
|
[[union]], |
||||
|
[[union-directive]], |
||||
|
[[yystype]]]])dnl |
||||
|
m4_case(b4_percent_define_get([[api.value.type]]), |
||||
|
[union], [b4_value_type_setup_union])]) |
||||
|
|
||||
|
|
||||
|
# b4_value_type_setup |
||||
|
# ------------------- |
||||
|
# Check if api.value.type is properly defined, and possibly prepare |
||||
|
# its use. |
||||
|
b4_define_silent([b4_value_type_setup], |
||||
|
[ |
||||
|
# Define default value. |
||||
|
b4_percent_define_ifdef([[api.value.type]], [], |
||||
|
[# %union => api.value.type=union-directive |
||||
|
m4_ifdef([b4_union_members], |
||||
|
[m4_define([b4_percent_define_kind(api.value.type)], [keyword]) |
||||
|
m4_define([b4_percent_define(api.value.type)], [union-directive])], |
||||
|
[# no tag seen => api.value.type={int} |
||||
|
m4_if(b4_tag_seen_flag, 0, |
||||
|
[m4_define([b4_percent_define_kind(api.value.type)], [code]) |
||||
|
m4_define([b4_percent_define(api.value.type)], [int])], |
||||
|
[# otherwise api.value.type=yystype |
||||
|
m4_define([b4_percent_define_kind(api.value.type)], [keyword]) |
||||
|
m4_define([b4_percent_define(api.value.type)], [yystype])])])]) |
||||
|
|
||||
|
# Set up. |
||||
|
m4_bmatch(b4_percent_define_get_kind([[api.value.type]]), |
||||
|
[keyword], [_b4_value_type_setup_keyword]) |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
## ----------------- ## |
||||
|
## Semantic Values. ## |
||||
|
## ----------------- ## |
||||
|
|
||||
|
|
||||
|
# b4_symbol_value(VAL, [SYMBOL-NUM], [TYPE-TAG]) |
||||
|
# ---------------------------------------------- |
||||
|
# See README. FIXME: factor in c-like? |
||||
|
m4_define([b4_symbol_value], |
||||
|
[m4_ifval([$3], |
||||
|
[($1.$3)], |
||||
|
[m4_ifval([$2], |
||||
|
[b4_symbol_if([$2], [has_type], |
||||
|
[($1.b4_symbol([$2], [type]))], |
||||
|
[$1])], |
||||
|
[$1])])]) |
||||
|
|
||||
|
# b4_lhs_value(SYMBOL-NUM, [TYPE]) |
||||
|
# -------------------------------- |
||||
|
# See README. |
||||
|
m4_define([b4_lhs_value], |
||||
|
[b4_symbol_value([yyval], [$1], [$2])]) |
||||
|
|
||||
|
|
||||
|
# b4_rhs_value(RULE-LENGTH, POS, SYMBOL-NUM, [TYPE]) |
||||
|
# -------------------------------------------------- |
||||
|
# See README. |
||||
|
# |
||||
|
# In this simple implementation, %token and %type have class names |
||||
|
# between the angle brackets. |
||||
|
m4_define([b4_rhs_value], |
||||
|
[b4_symbol_value([(yystack.valueAt (b4_subtract([$1], [$2])))], [$3], [$4])]) |
||||
|
|
||||
|
|
||||
|
# b4_lhs_location() |
||||
|
# ----------------- |
||||
|
# Expansion of @$. |
||||
|
m4_define([b4_lhs_location], |
||||
|
[(yyloc)]) |
||||
|
|
||||
|
|
||||
|
# b4_rhs_location(RULE-LENGTH, POS) |
||||
|
# --------------------------------- |
||||
|
# Expansion of @POS, where the current rule has RULE-LENGTH symbols |
||||
|
# on RHS. |
||||
|
m4_define([b4_rhs_location], |
||||
|
[yystack.locationAt (b4_subtract($@))]) |
||||
|
|
||||
|
|
||||
|
# b4_lex_param |
||||
|
# b4_parse_param |
||||
|
# -------------- |
||||
|
# If defined, b4_lex_param arrives double quoted, but below we prefer |
||||
|
# it to be single quoted. Same for b4_parse_param. |
||||
|
|
||||
|
# TODO: should be in bison.m4 |
||||
|
m4_define_default([b4_lex_param], [[]])) |
||||
|
m4_define([b4_lex_param], b4_lex_param)) |
||||
|
m4_define([b4_parse_param], b4_parse_param)) |
||||
|
|
||||
|
# b4_lex_param_decl |
||||
|
# ------------------- |
||||
|
# Extra formal arguments of the constructor. |
||||
|
m4_define([b4_lex_param_decl], |
||||
|
[m4_ifset([b4_lex_param], |
||||
|
[b4_remove_comma([$1], |
||||
|
b4_param_decls(b4_lex_param))], |
||||
|
[$1])]) |
||||
|
|
||||
|
m4_define([b4_param_decls], |
||||
|
[m4_map([b4_param_decl], [$@])]) |
||||
|
m4_define([b4_param_decl], [, $1]) |
||||
|
|
||||
|
m4_define([b4_remove_comma], [m4_ifval(m4_quote($1), [$1, ], [])m4_shift2($@)]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_decl |
||||
|
# ------------------- |
||||
|
# Extra formal arguments of the constructor. |
||||
|
m4_define([b4_parse_param_decl], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[b4_remove_comma([$1], |
||||
|
b4_param_decls(b4_parse_param))], |
||||
|
[$1])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_lex_param_call |
||||
|
# ------------------- |
||||
|
# Delegating the lexer parameters to the lexer constructor. |
||||
|
m4_define([b4_lex_param_call], |
||||
|
[m4_ifset([b4_lex_param], |
||||
|
[b4_remove_comma([$1], |
||||
|
b4_param_calls(b4_lex_param))], |
||||
|
[$1])]) |
||||
|
m4_define([b4_param_calls], |
||||
|
[m4_map([b4_param_call], [$@])]) |
||||
|
m4_define([b4_param_call], [, $2]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_cons |
||||
|
# ------------------- |
||||
|
# Extra initialisations of the constructor. |
||||
|
m4_define([b4_parse_param_cons], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[b4_constructor_calls(b4_parse_param)])]) |
||||
|
|
||||
|
m4_define([b4_constructor_calls], |
||||
|
[m4_map([b4_constructor_call], [$@])]) |
||||
|
m4_define([b4_constructor_call], |
||||
|
[this.$2 = $2; |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_vars |
||||
|
# ------------------- |
||||
|
# Extra instance variables. |
||||
|
m4_define([b4_parse_param_vars], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[ |
||||
|
/* User arguments. */ |
||||
|
b4_var_decls(b4_parse_param)])]) |
||||
|
|
||||
|
m4_define([b4_var_decls], |
||||
|
[m4_map_sep([b4_var_decl], [ |
||||
|
], [$@])]) |
||||
|
m4_define([b4_var_decl], |
||||
|
[ protected $1;]) |
||||
|
|
||||
|
|
||||
|
# b4_public_types_declare |
||||
|
# ----------------------- |
||||
|
# Define the public types: token, semantic value, location, and so forth. |
||||
|
# Depending on %define token_lex, may be output in the header or source file. |
||||
|
m4_define([b4_public_types_declare], |
||||
|
[[ |
||||
|
alias Symbol = ]b4_parser_class[.Symbol; |
||||
|
alias Value = ]b4_yystype[;]b4_locations_if([[ |
||||
|
alias Location = ]b4_location_type[; |
||||
|
alias Position = ]b4_position_type[;]b4_push_if([[ |
||||
|
alias PUSH_MORE = ]b4_parser_class[.YYPUSH_MORE; |
||||
|
alias ABORT = ]b4_parser_class[.YYABORT; |
||||
|
alias ACCEPT = ]b4_parser_class[.YYACCEPT;]])[]])[ |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_basic_symbol_constructor_define |
||||
|
# ---------------------------------- |
||||
|
# Create Symbol struct constructors for all the visible types. |
||||
|
m4_define([b4_basic_symbol_constructor_define], |
||||
|
[b4_token_visible_if([$1], |
||||
|
[[ this(TokenKind token]b4_symbol_if([$1], [has_type], |
||||
|
[[, ]b4_union_if([], [[typeof(YYSemanticType.]])b4_symbol([$1], [type])dnl |
||||
|
[]b4_union_if([], [[) ]])[ val]])[]b4_locations_if([[, Location loc]])[) |
||||
|
{ |
||||
|
kind = yytranslate_(token);]b4_union_if([b4_symbol_if([$1], [has_type], [[ |
||||
|
static foreach (member; __traits(allMembers, YYSemanticType)) |
||||
|
{ |
||||
|
static if (is(typeof(mixin("value_." ~ member)) == ]b4_symbol([$1], [type])[)) |
||||
|
{ |
||||
|
mixin("value_." ~ member ~ " = val;"); |
||||
|
} |
||||
|
}]])], [b4_symbol_if([$1], [has_type], [[ |
||||
|
value_.]b4_symbol([$1], [type])[ = val;]])])[]b4_locations_if([ |
||||
|
location_ = loc;])[ |
||||
|
} |
||||
|
]])]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_type_define |
||||
|
# --------------------- |
||||
|
# Define symbol_type, the external type for symbols used for symbol |
||||
|
# constructors. |
||||
|
m4_define([b4_symbol_type_define], |
||||
|
[[ |
||||
|
/** |
||||
|
* A complete symbol |
||||
|
*/ |
||||
|
struct Symbol |
||||
|
{ |
||||
|
private SymbolKind kind; |
||||
|
private Value value_;]b4_locations_if([[ |
||||
|
private Location location_;]])[ |
||||
|
|
||||
|
]b4_type_foreach([b4_basic_symbol_constructor_define])[ |
||||
|
SymbolKind token() { return kind; } |
||||
|
Value value() { return value_; }]b4_locations_if([[ |
||||
|
Location location() { return location_; }]])[ |
||||
|
]b4_token_ctor_if([b4_token_constructor_define])[ |
||||
|
} |
||||
|
]]) |
||||
2763
tools/winflexbison/data/skeletons/glr.c
File diff suppressed because it is too large
View File
@ -0,0 +1,397 @@ |
|||||
|
# C++ GLR skeleton for Bison
|
||||
|
|
||||
|
# Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
|
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify
|
||||
|
# it under the terms of the GNU General Public License as published by
|
||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||
|
# (at your option) any later version.
|
||||
|
#
|
||||
|
# This program is distributed in the hope that it will be useful,
|
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
# GNU General Public License for more details.
|
||||
|
#
|
||||
|
# You should have received a copy of the GNU General Public License
|
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
|
||||
|
# This skeleton produces a C++ class that encapsulates a C glr parser.
|
||||
|
# This is in order to reduce the maintenance burden. The glr.c
|
||||
|
# skeleton is clean and pure enough so that there are no real
|
||||
|
# problems. The C++ interface is the same as that of lalr1.cc. In
|
||||
|
# fact, glr.c can replace yacc.c without the user noticing any
|
||||
|
# difference, and similarly for glr.cc replacing lalr1.cc.
|
||||
|
#
|
||||
|
# The passing of parse-params
|
||||
|
#
|
||||
|
# The additional arguments are stored as members of the parser
|
||||
|
# object, yyparser. The C routines need to carry yyparser
|
||||
|
# throughout the C parser; that's easy: make yyparser an
|
||||
|
# additional parse-param. But because the C++ skeleton needs to
|
||||
|
# know the "real" original parse-param, we save them
|
||||
|
# (b4_parse_param_orig). Note that b4_parse_param is overquoted
|
||||
|
# (and c.m4 strips one level of quotes). This is a PITA, and
|
||||
|
# explains why there are so many levels of quotes.
|
||||
|
#
|
||||
|
# The locations
|
||||
|
#
|
||||
|
# We use location.cc just like lalr1.cc, but because glr.c stores
|
||||
|
# the locations in a union, the position and location classes
|
||||
|
# must not have a constructor. Therefore, contrary to lalr1.cc, we
|
||||
|
# must not define "b4_location_constructors". As a consequence the
|
||||
|
# user must initialize the first positions (in particular the
|
||||
|
# filename member).
|
||||
|
|
||||
|
# We require a pure interface.
|
||||
|
m4_define([b4_pure_flag], [1]) |
||||
|
|
||||
|
m4_include(b4_skeletonsdir/[c++.m4]) |
||||
|
b4_bison_locations_if([m4_include(b4_skeletonsdir/[location.cc])]) |
||||
|
|
||||
|
m4_define([b4_parser_class], |
||||
|
[b4_percent_define_get([[api.parser.class]])]) |
||||
|
|
||||
|
# Save the parse parameters.
|
||||
|
m4_define([b4_parse_param_orig], m4_defn([b4_parse_param])) |
||||
|
|
||||
|
# b4_parse_param_wrap
|
||||
|
# -------------------
|
||||
|
# New ones.
|
||||
|
m4_ifset([b4_parse_param], |
||||
|
[m4_define([b4_parse_param_wrap], |
||||
|
[[b4_namespace_ref::b4_parser_class[& yyparser], [[yyparser]]],] |
||||
|
m4_defn([b4_parse_param]))], |
||||
|
[m4_define([b4_parse_param_wrap], |
||||
|
[[b4_namespace_ref::b4_parser_class[& yyparser], [[yyparser]]]]) |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_yy_symbol_print_define
|
||||
|
# -------------------------
|
||||
|
# Bypass the default implementation to generate the "yy_symbol_print"
|
||||
|
# and "yy_symbol_value_print" functions.
|
||||
|
m4_define([b4_yy_symbol_print_define], |
||||
|
[[/*--------------------.
|
||||
|
| Print this symbol. | |
||||
|
`--------------------*/ |
||||
|
|
||||
|
static void |
||||
|
yy_symbol_print (FILE *, ]b4_namespace_ref::b4_parser_class[::symbol_kind_type yytoken, |
||||
|
const ]b4_namespace_ref::b4_parser_class[::value_type *yyvaluep]b4_locations_if([[, |
||||
|
const ]b4_namespace_ref::b4_parser_class[::location_type *yylocationp]])[]b4_user_formals[) |
||||
|
{ |
||||
|
]b4_parse_param_use[]dnl |
||||
|
[ yyparser.yy_symbol_print_ (yytoken, yyvaluep]b4_locations_if([, yylocationp])[); |
||||
|
} |
||||
|
]])[ |
||||
|
|
||||
|
# Hijack the initial action to initialize the locations.
|
||||
|
]b4_bison_locations_if([m4_define([b4_initial_action], |
||||
|
[yylloc.initialize ();]m4_ifdef([b4_initial_action], [ |
||||
|
m4_defn([b4_initial_action])]))])[ |
||||
|
|
||||
|
# Hijack the post prologue to declare yyerror.
|
||||
|
]m4_append([b4_post_prologue], |
||||
|
[b4_syncline([@oline@], [@ofile@])dnl |
||||
|
[static void |
||||
|
yyerror (]b4_locations_if([[const ]b4_namespace_ref::b4_parser_class[::location_type *yylocationp, |
||||
|
]])[]m4_ifset([b4_parse_param], [b4_formals(b4_parse_param), |
||||
|
])[const char* msg);]])[ |
||||
|
|
||||
|
# Inserted before the epilogue to define implementations (yyerror, parser member
|
||||
|
# functions etc.).
|
||||
|
]m4_define([b4_glr_cc_pre_epilogue], |
||||
|
[b4_syncline([@oline@], [@ofile@])dnl |
||||
|
[ |
||||
|
/*------------------.
|
||||
|
| Report an error. | |
||||
|
`------------------*/ |
||||
|
|
||||
|
static void |
||||
|
yyerror (]b4_locations_if([[const ]b4_namespace_ref::b4_parser_class[::location_type *yylocationp, |
||||
|
]])[]m4_ifset([b4_parse_param], [b4_formals(b4_parse_param), |
||||
|
])[const char* msg) |
||||
|
{ |
||||
|
]b4_parse_param_use[]dnl |
||||
|
[ yyparser.error (]b4_locations_if([[*yylocationp, ]])[msg); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
]b4_namespace_open[ |
||||
|
]dnl In this section, the parse params are the original parse_params. |
||||
|
m4_pushdef([b4_parse_param], m4_defn([b4_parse_param_orig]))dnl |
||||
|
[ /// Build a parser object.
|
||||
|
]b4_parser_class::b4_parser_class[ (]b4_parse_param_decl[)]m4_ifset([b4_parse_param], [ |
||||
|
:])[ |
||||
|
#if ]b4_api_PREFIX[DEBUG
|
||||
|
]m4_ifset([b4_parse_param], [ ], [ :])[yycdebug_ (&std::cerr)]m4_ifset([b4_parse_param], [,])[ |
||||
|
#endif]b4_parse_param_cons[
|
||||
|
{} |
||||
|
|
||||
|
]b4_parser_class::~b4_parser_class[ () |
||||
|
{} |
||||
|
|
||||
|
]b4_parser_class[::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW |
||||
|
{} |
||||
|
|
||||
|
int |
||||
|
]b4_parser_class[::operator() () |
||||
|
{ |
||||
|
return parse (); |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
]b4_parser_class[::parse () |
||||
|
{ |
||||
|
return ::yy_parse_impl (*this]b4_user_args[); |
||||
|
} |
||||
|
|
||||
|
#if ]b4_api_PREFIX[DEBUG
|
||||
|
/*--------------------.
|
||||
|
| Print this symbol. | |
||||
|
`--------------------*/ |
||||
|
|
||||
|
void |
||||
|
]b4_parser_class[::yy_symbol_value_print_ (symbol_kind_type yykind, |
||||
|
const value_type* yyvaluep]b4_locations_if([[, |
||||
|
const location_type* yylocationp]])[) const |
||||
|
{]b4_locations_if([[ |
||||
|
YY_USE (yylocationp);]])[ |
||||
|
YY_USE (yyvaluep); |
||||
|
std::ostream& yyo = debug_stream (); |
||||
|
std::ostream& yyoutput = yyo; |
||||
|
YY_USE (yyoutput); |
||||
|
]b4_symbol_actions([printer])[ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void |
||||
|
]b4_parser_class[::yy_symbol_print_ (symbol_kind_type yykind, |
||||
|
const value_type* yyvaluep]b4_locations_if([[, |
||||
|
const location_type* yylocationp]])[) const |
||||
|
{ |
||||
|
*yycdebug_ << (yykind < YYNTOKENS ? "token" : "nterm") |
||||
|
<< ' ' << yysymbol_name (yykind) << " ("]b4_locations_if([[ |
||||
|
<< *yylocationp << ": "]])[; |
||||
|
yy_symbol_value_print_ (yykind, yyvaluep]b4_locations_if([[, yylocationp]])[); |
||||
|
*yycdebug_ << ')'; |
||||
|
} |
||||
|
|
||||
|
std::ostream& |
||||
|
]b4_parser_class[::debug_stream () const |
||||
|
{ |
||||
|
return *yycdebug_; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
]b4_parser_class[::set_debug_stream (std::ostream& o) |
||||
|
{ |
||||
|
yycdebug_ = &o; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
]b4_parser_class[::debug_level_type |
||||
|
]b4_parser_class[::debug_level () const |
||||
|
{ |
||||
|
return yydebug; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
]b4_parser_class[::set_debug_level (debug_level_type l) |
||||
|
{ |
||||
|
// Actually, it is yydebug which is really used.
|
||||
|
yydebug = l; |
||||
|
} |
||||
|
|
||||
|
#endif
|
||||
|
]m4_popdef([b4_parse_param])dnl |
||||
|
b4_namespace_close[]dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
m4_define([b4_define_symbol_kind], |
||||
|
[m4_format([#define %-15s %s], |
||||
|
b4_symbol($][1, kind_base), |
||||
|
b4_namespace_ref[::]b4_parser_class[::symbol_kind::]b4_symbol($1, kind_base)) |
||||
|
]) |
||||
|
|
||||
|
# b4_glr_cc_setup
|
||||
|
# ---------------
|
||||
|
# Setup redirections for glr.c: Map the names used in c.m4 to the ones used
|
||||
|
# in c++.m4.
|
||||
|
m4_define([b4_glr_cc_setup], |
||||
|
[[]b4_attribute_define[ |
||||
|
]b4_null_define[ |
||||
|
|
||||
|
// This skeleton is based on C, yet compiles it as C++.
|
||||
|
// So expect warnings about C style casts.
|
||||
|
#if defined __clang__ && 306 <= __clang_major__ * 100 + __clang_minor__
|
||||
|
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
|
#elif defined __GNUC__ && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
|
||||
|
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
|
#endif
|
||||
|
|
||||
|
// On MacOS, PTRDIFF_MAX is defined as long long, which Clang's
|
||||
|
// -pedantic reports as being a C++11 extension.
|
||||
|
#if defined __APPLE__ && YY_CPLUSPLUS < 201103L \
|
||||
|
&& defined __clang__ && 4 <= __clang_major__ |
||||
|
# pragma clang diagnostic ignored "-Wc++11-long-long"
|
||||
|
#endif
|
||||
|
|
||||
|
#undef ]b4_symbol(empty, [id])[
|
||||
|
#define ]b4_symbol(empty, [id])[ ]b4_namespace_ref[::]b4_parser_class[::token::]b4_symbol(empty, [id])[
|
||||
|
#undef ]b4_symbol(eof, [id])[
|
||||
|
#define ]b4_symbol(eof, [id])[ ]b4_namespace_ref[::]b4_parser_class[::token::]b4_symbol(eof, [id])[
|
||||
|
#undef ]b4_symbol(error, [id])[
|
||||
|
#define ]b4_symbol(error, [id])[ ]b4_namespace_ref[::]b4_parser_class[::token::]b4_symbol(error, [id])[
|
||||
|
|
||||
|
#ifndef ]b4_api_PREFIX[STYPE
|
||||
|
# define ]b4_api_PREFIX[STYPE ]b4_namespace_ref[::]b4_parser_class[::value_type
|
||||
|
#endif
|
||||
|
#ifndef ]b4_api_PREFIX[LTYPE
|
||||
|
# define ]b4_api_PREFIX[LTYPE ]b4_namespace_ref[::]b4_parser_class[::location_type
|
||||
|
#endif
|
||||
|
|
||||
|
typedef ]b4_namespace_ref[::]b4_parser_class[::symbol_kind_type yysymbol_kind_t; |
||||
|
|
||||
|
// Expose C++ symbol kinds to C.
|
||||
|
]b4_define_symbol_kind(-2)dnl |
||||
|
b4_symbol_foreach([b4_define_symbol_kind])])[ |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
m4_define([b4_undef_symbol_kind], |
||||
|
[[#undef ]b4_symbol($1, kind_base)[ |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_glr_cc_cleanup
|
||||
|
# -----------------
|
||||
|
# Remove redirections for glr.c.
|
||||
|
m4_define([b4_glr_cc_cleanup], |
||||
|
[[#undef ]b4_symbol(empty, [id])[ |
||||
|
#undef ]b4_symbol(eof, [id])[
|
||||
|
#undef ]b4_symbol(error, [id])[
|
||||
|
|
||||
|
]b4_undef_symbol_kind(-2)dnl |
||||
|
b4_symbol_foreach([b4_undef_symbol_kind])dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_shared_declarations(hh|cc)
|
||||
|
# -----------------------------
|
||||
|
# Declaration that might either go into the header (if --header, $1 = hh)
|
||||
|
# or in the implementation file.
|
||||
|
m4_define([b4_shared_declarations], |
||||
|
[m4_pushdef([b4_parse_param], m4_defn([b4_parse_param_orig]))dnl |
||||
|
b4_percent_code_get([[requires]])[ |
||||
|
#include <iostream>
|
||||
|
#include <stdexcept>
|
||||
|
#include <string>
|
||||
|
|
||||
|
]b4_cxx_portability[ |
||||
|
]m4_ifdef([b4_location_include], |
||||
|
[[# include ]b4_location_include])[ |
||||
|
]b4_variant_if([b4_variant_includes])[ |
||||
|
|
||||
|
// Whether we are compiled with exception support.
|
||||
|
#ifndef YY_EXCEPTIONS
|
||||
|
# if defined __GNUC__ && !defined __EXCEPTIONS
|
||||
|
# define YY_EXCEPTIONS 0
|
||||
|
# else
|
||||
|
# define YY_EXCEPTIONS 1
|
||||
|
# endif
|
||||
|
#endif
|
||||
|
|
||||
|
]b4_YYDEBUG_define[ |
||||
|
|
||||
|
]b4_namespace_open[ |
||||
|
|
||||
|
]b4_bison_locations_if([m4_ifndef([b4_location_file], |
||||
|
[b4_location_define])])[ |
||||
|
|
||||
|
/// A Bison parser.
|
||||
|
class ]b4_parser_class[ |
||||
|
{ |
||||
|
public: |
||||
|
]b4_public_types_declare[ |
||||
|
|
||||
|
/// Build a parser object.
|
||||
|
]b4_parser_class[ (]b4_parse_param_decl[); |
||||
|
virtual ~]b4_parser_class[ (); |
||||
|
|
||||
|
/// Parse. An alias for parse ().
|
||||
|
/// \returns 0 iff parsing succeeded.
|
||||
|
int operator() (); |
||||
|
|
||||
|
/// Parse.
|
||||
|
/// \returns 0 iff parsing succeeded.
|
||||
|
virtual int parse (); |
||||
|
|
||||
|
#if ]b4_api_PREFIX[DEBUG
|
||||
|
/// The current debugging stream.
|
||||
|
std::ostream& debug_stream () const; |
||||
|
/// Set the current debugging stream.
|
||||
|
void set_debug_stream (std::ostream &); |
||||
|
|
||||
|
/// Type for debugging levels.
|
||||
|
typedef int debug_level_type; |
||||
|
/// The current debugging level.
|
||||
|
debug_level_type debug_level () const; |
||||
|
/// Set the current debugging level.
|
||||
|
void set_debug_level (debug_level_type l); |
||||
|
#endif
|
||||
|
|
||||
|
/// Report a syntax error.]b4_locations_if([[
|
||||
|
/// \param loc where the syntax error is found.]])[
|
||||
|
/// \param msg a description of the syntax error.
|
||||
|
virtual void error (]b4_locations_if([[const location_type& loc, ]])[const std::string& msg); |
||||
|
|
||||
|
# if ]b4_api_PREFIX[DEBUG
|
||||
|
public: |
||||
|
/// \brief Report a symbol value on the debug stream.
|
||||
|
/// \param yykind The symbol kind.
|
||||
|
/// \param yyvaluep Its semantic value.]b4_locations_if([[
|
||||
|
/// \param yylocationp Its location.]])[
|
||||
|
virtual void yy_symbol_value_print_ (symbol_kind_type yykind, |
||||
|
const value_type* yyvaluep]b4_locations_if([[, |
||||
|
const location_type* yylocationp]])[) const; |
||||
|
/// \brief Report a symbol on the debug stream.
|
||||
|
/// \param yykind The symbol kind.
|
||||
|
/// \param yyvaluep Its semantic value.]b4_locations_if([[
|
||||
|
/// \param yylocationp Its location.]])[
|
||||
|
virtual void yy_symbol_print_ (symbol_kind_type yykind, |
||||
|
const value_type* yyvaluep]b4_locations_if([[, |
||||
|
const location_type* yylocationp]])[) const; |
||||
|
private: |
||||
|
/// Debug stream.
|
||||
|
std::ostream* yycdebug_; |
||||
|
#endif
|
||||
|
|
||||
|
]b4_parse_param_vars[ |
||||
|
}; |
||||
|
|
||||
|
]b4_namespace_close[ |
||||
|
|
||||
|
]b4_percent_code_get([[provides]])[ |
||||
|
]m4_popdef([b4_parse_param])dnl |
||||
|
])[ |
||||
|
|
||||
|
]b4_header_if( |
||||
|
[b4_output_begin([b4_spec_header_file]) |
||||
|
b4_copyright([Skeleton interface for Bison GLR parsers in C++], |
||||
|
[2002-2015, 2018-2021])[ |
||||
|
// C++ GLR parser skeleton written by Akim Demaille.
|
||||
|
|
||||
|
]b4_disclaimer[ |
||||
|
]b4_cpp_guard_open([b4_spec_mapped_header_file])[ |
||||
|
]b4_shared_declarations[ |
||||
|
]b4_cpp_guard_close([b4_spec_mapped_header_file])[ |
||||
|
]b4_output_end]) |
||||
|
|
||||
|
# Let glr.c (and b4_shared_declarations) believe that the user
|
||||
|
# arguments include the parser itself.
|
||||
|
m4_pushdef([b4_parse_param], m4_defn([b4_parse_param_wrap])) |
||||
|
m4_include(b4_skeletonsdir/[glr.c]) |
||||
|
m4_popdef([b4_parse_param]) |
||||
3533
tools/winflexbison/data/skeletons/glr2.cc
File diff suppressed because it is too large
View File
@ -0,0 +1,27 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# Java skeleton dispatching for Bison. |
||||
|
|
||||
|
# Copyright (C) 2007, 2009-2015, 2018-2021 Free Software Foundation, |
||||
|
# Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
b4_glr_if( [b4_complain([%%glr-parser not supported for Java])]) |
||||
|
b4_nondeterministic_if([b4_complain([%%nondeterministic-parser not supported for Java])]) |
||||
|
|
||||
|
m4_define_default([b4_used_skeleton], [b4_skeletonsdir/[lalr1.java]]) |
||||
|
m4_define_default([b4_skeleton], ["b4_basename(b4_used_skeleton)"]) |
||||
|
|
||||
|
m4_include(b4_used_skeleton) |
||||
@ -0,0 +1,502 @@ |
|||||
|
-*- Autoconf -*- |
||||
|
|
||||
|
# Java language support for Bison |
||||
|
|
||||
|
# Copyright (C) 2007-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
m4_include(b4_skeletonsdir/[c-like.m4]) |
||||
|
|
||||
|
|
||||
|
# b4_list2(LIST1, LIST2) |
||||
|
# ---------------------- |
||||
|
# Join two lists with a comma if necessary. |
||||
|
m4_define([b4_list2], |
||||
|
[$1[]m4_ifval(m4_quote($1), [m4_ifval(m4_quote($2), [[, ]])])[]$2]) |
||||
|
|
||||
|
|
||||
|
# b4_percent_define_get3(DEF, PRE, POST, NOT) |
||||
|
# ------------------------------------------- |
||||
|
# Expand to the value of DEF surrounded by PRE and POST if it's %define'ed, |
||||
|
# otherwise NOT. |
||||
|
m4_define([b4_percent_define_get3], |
||||
|
[m4_ifval(m4_quote(b4_percent_define_get([$1])), |
||||
|
[$2[]b4_percent_define_get([$1])[]$3], [$4])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_flag_value(BOOLEAN-FLAG) |
||||
|
# --------------------------- |
||||
|
m4_define([b4_flag_value], [b4_flag_if([$1], [true], [false])]) |
||||
|
|
||||
|
|
||||
|
# b4_parser_class_declaration |
||||
|
# --------------------------- |
||||
|
# The declaration of the parser class ("class YYParser"), with all its |
||||
|
# qualifiers/annotations. |
||||
|
b4_percent_define_default([[api.parser.abstract]], [[false]]) |
||||
|
b4_percent_define_default([[api.parser.final]], [[false]]) |
||||
|
b4_percent_define_default([[api.parser.public]], [[false]]) |
||||
|
b4_percent_define_default([[api.parser.strictfp]], [[false]]) |
||||
|
|
||||
|
m4_define([b4_parser_class_declaration], |
||||
|
[b4_percent_define_get3([api.parser.annotations], [], [ ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.public], [public ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.abstract], [abstract ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.final], [final ])dnl |
||||
|
b4_percent_define_flag_if([api.parser.strictfp], [strictfp ])dnl |
||||
|
[class ]b4_parser_class[]dnl |
||||
|
b4_percent_define_get3([api.parser.extends], [ extends ])dnl |
||||
|
b4_percent_define_get3([api.parser.implements], [ implements ])]) |
||||
|
|
||||
|
|
||||
|
# b4_lexer_if(TRUE, FALSE) |
||||
|
# ------------------------ |
||||
|
m4_define([b4_lexer_if], |
||||
|
[b4_percent_code_ifdef([[lexer]], [$1], [$2])]) |
||||
|
|
||||
|
|
||||
|
# b4_identification |
||||
|
# ----------------- |
||||
|
m4_define([b4_identification], |
||||
|
[[ /** Version number for the Bison executable that generated this parser. */ |
||||
|
public static final String bisonVersion = "]b4_version_string["; |
||||
|
|
||||
|
/** Name of the skeleton that generated this parser. */ |
||||
|
public static final String bisonSkeleton = ]b4_skeleton[; |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
## ------------ ## |
||||
|
## Data types. ## |
||||
|
## ------------ ## |
||||
|
|
||||
|
# b4_int_type(MIN, MAX) |
||||
|
# --------------------- |
||||
|
# Return the smallest int type able to handle numbers ranging from |
||||
|
# MIN to MAX (included). |
||||
|
m4_define([b4_int_type], |
||||
|
[m4_if(b4_ints_in($@, [-128], [127]), [1], [byte], |
||||
|
b4_ints_in($@, [-32768], [32767]), [1], [short], |
||||
|
[int])]) |
||||
|
|
||||
|
# b4_int_type_for(NAME) |
||||
|
# --------------------- |
||||
|
# Return the smallest int type able to handle numbers ranging from |
||||
|
# 'NAME_min' to 'NAME_max' (included). |
||||
|
m4_define([b4_int_type_for], |
||||
|
[b4_int_type($1_min, $1_max)]) |
||||
|
|
||||
|
# b4_null |
||||
|
# ------- |
||||
|
m4_define([b4_null], [null]) |
||||
|
|
||||
|
|
||||
|
# b4_typed_parser_table_define(TYPE, NAME, DATA, COMMENT) |
||||
|
# ------------------------------------------------------- |
||||
|
# We use intermediate functions (e.g., yypact_init) to work around the |
||||
|
# 64KB limit for JVM methods. See |
||||
|
# https://lists.gnu.org/r/help-bison/2008-11/msg00004.html. |
||||
|
m4_define([b4_typed_parser_table_define], |
||||
|
[m4_ifval([$4], [b4_comment([$4]) |
||||
|
])dnl |
||||
|
[private static final ]$1[[] yy$2_ = yy$2_init(); |
||||
|
private static final ]$1[[] yy$2_init() |
||||
|
{ |
||||
|
return new ]$1[[] |
||||
|
{ |
||||
|
]$3[ |
||||
|
}; |
||||
|
}]]) |
||||
|
|
||||
|
|
||||
|
# b4_integral_parser_table_define(NAME, DATA, COMMENT) |
||||
|
#----------------------------------------------------- |
||||
|
m4_define([b4_integral_parser_table_define], |
||||
|
[b4_typed_parser_table_define([b4_int_type_for([$2])], [$1], [$2], [$3])]) |
||||
|
|
||||
|
|
||||
|
## ------------- ## |
||||
|
## Token kinds. ## |
||||
|
## ------------- ## |
||||
|
|
||||
|
|
||||
|
# b4_token_enum(TOKEN-NUM) |
||||
|
# ------------------------ |
||||
|
# Output the definition of this token as an enum. |
||||
|
m4_define([b4_token_enum], |
||||
|
[b4_token_visible_if([$1], |
||||
|
[m4_format([[ /** Token %s, to be returned by the scanner. */ |
||||
|
static final int %s = %s%s; |
||||
|
]], |
||||
|
b4_symbol([$1], [tag]), |
||||
|
b4_symbol([$1], [id]), |
||||
|
b4_symbol([$1], b4_api_token_raw_if([[number]], [[code]])))])]) |
||||
|
|
||||
|
|
||||
|
# b4_token_enums |
||||
|
# -------------- |
||||
|
# Output the definition of the tokens (if there are) as enums. |
||||
|
m4_define([b4_token_enums], |
||||
|
[b4_any_token_visible_if([ /* Token kinds. */ |
||||
|
b4_symbol_foreach([b4_token_enum])])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## -------------- ## |
||||
|
## Symbol kinds. ## |
||||
|
## -------------- ## |
||||
|
|
||||
|
|
||||
|
# b4_symbol_kind(NUM) |
||||
|
# ------------------- |
||||
|
m4_define([b4_symbol_kind], |
||||
|
[SymbolKind.b4_symbol_kind_base($@)]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_enum(SYMBOL-NUM) |
||||
|
# -------------------------- |
||||
|
# Output the definition of this symbol as an enum. |
||||
|
m4_define([b4_symbol_enum], |
||||
|
[m4_format([ %-30s %s], |
||||
|
m4_format([[%s(%s)%s]], |
||||
|
b4_symbol([$1], [kind_base]), |
||||
|
[$1], |
||||
|
m4_if([$1], b4_last_symbol, [[;]], [[,]])), |
||||
|
[b4_symbol_tag_comment([$1])])]) |
||||
|
|
||||
|
|
||||
|
# b4_declare_symbol_enum |
||||
|
# ---------------------- |
||||
|
# The definition of the symbol internal numbers as an enum. |
||||
|
m4_define([b4_declare_symbol_enum], |
||||
|
[[ public enum SymbolKind |
||||
|
{ |
||||
|
]b4_symbol_foreach([b4_symbol_enum])[ |
||||
|
|
||||
|
private final int yycode_; |
||||
|
|
||||
|
SymbolKind (int n) { |
||||
|
this.yycode_ = n; |
||||
|
} |
||||
|
|
||||
|
private static final SymbolKind[] values_ = { |
||||
|
]m4_map_args_sep([b4_symbol_kind(], [)], [, |
||||
|
], b4_symbol_numbers)[ |
||||
|
}; |
||||
|
|
||||
|
static final SymbolKind get(int code) { |
||||
|
return values_[code]; |
||||
|
} |
||||
|
|
||||
|
public final int getCode() { |
||||
|
return this.yycode_; |
||||
|
} |
||||
|
|
||||
|
]b4_parse_error_bmatch( |
||||
|
[simple\|verbose], |
||||
|
[[ /* Return YYSTR after stripping away unnecessary quotes and |
||||
|
backslashes, so that it's suitable for yyerror. The heuristic is |
||||
|
that double-quoting is unnecessary unless the string contains an |
||||
|
apostrophe, a comma, or backslash (other than backslash-backslash). |
||||
|
YYSTR is taken from yytname. */ |
||||
|
private static String yytnamerr_(String yystr) |
||||
|
{ |
||||
|
if (yystr.charAt (0) == '"') |
||||
|
{ |
||||
|
StringBuffer yyr = new StringBuffer(); |
||||
|
strip_quotes: for (int i = 1; i < yystr.length(); i++) |
||||
|
switch (yystr.charAt(i)) |
||||
|
{ |
||||
|
case '\'': |
||||
|
case ',': |
||||
|
break strip_quotes; |
||||
|
|
||||
|
case '\\': |
||||
|
if (yystr.charAt(++i) != '\\') |
||||
|
break strip_quotes; |
||||
|
/* Fall through. */ |
||||
|
default: |
||||
|
yyr.append(yystr.charAt(i)); |
||||
|
break; |
||||
|
|
||||
|
case '"': |
||||
|
return yyr.toString(); |
||||
|
} |
||||
|
} |
||||
|
return yystr; |
||||
|
} |
||||
|
|
||||
|
/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. |
||||
|
First, the terminals, then, starting at \a YYNTOKENS_, nonterminals. */ |
||||
|
]b4_typed_parser_table_define([String], [tname], [b4_tname])[ |
||||
|
|
||||
|
/* The user-facing name of this symbol. */ |
||||
|
public final String getName() { |
||||
|
return yytnamerr_(yytname_[yycode_]); |
||||
|
} |
||||
|
]], |
||||
|
[custom\|detailed], |
||||
|
[[ /* YYNAMES_[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. |
||||
|
First, the terminals, then, starting at \a YYNTOKENS_, nonterminals. */ |
||||
|
]b4_typed_parser_table_define([String], [names], [b4_symbol_names])[ |
||||
|
|
||||
|
/* The user-facing name of this symbol. */ |
||||
|
public final String getName() { |
||||
|
return yynames_[yycode_]; |
||||
|
}]])[ |
||||
|
}; |
||||
|
]])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_case(ID, CODE, [COMMENTS]) |
||||
|
# ----------------------------- |
||||
|
# We need to fool Java's stupid unreachable code detection. |
||||
|
m4_define([b4_case], |
||||
|
[ case $1:m4_ifval([$3], [ b4_comment([$3])]) |
||||
|
if (yyn == $1) |
||||
|
$2; |
||||
|
break; |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_predicate_case(LABEL, CONDITIONS) |
||||
|
# ------------------------------------ |
||||
|
m4_define([b4_predicate_case], |
||||
|
[ case $1: |
||||
|
if (! ($2)) YYERROR; |
||||
|
break; |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
## -------- ## |
||||
|
## Checks. ## |
||||
|
## -------- ## |
||||
|
|
||||
|
b4_percent_define_check_kind([[api.value.type]], [code], [deprecated]) |
||||
|
|
||||
|
b4_percent_define_check_kind([[annotations]], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([[extends]], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([[implements]], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([[init_throws]], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([[lex_throws]], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([[api.parser.class]], [code], [deprecated]) |
||||
|
b4_percent_define_check_kind([[throws]], [code], [deprecated]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## ---------------- ## |
||||
|
## Default values. ## |
||||
|
## ---------------- ## |
||||
|
|
||||
|
m4_define([b4_yystype], [b4_percent_define_get([[api.value.type]])]) |
||||
|
b4_percent_define_default([[api.value.type]], [[Object]]) |
||||
|
b4_percent_define_default([[api.symbol.prefix]], [[S_]]) |
||||
|
|
||||
|
# b4_api_prefix, b4_api_PREFIX |
||||
|
# ---------------------------- |
||||
|
# Corresponds to %define api.prefix |
||||
|
b4_percent_define_default([[api.prefix]], [[YY]]) |
||||
|
m4_define([b4_api_prefix], |
||||
|
[b4_percent_define_get([[api.prefix]])]) |
||||
|
m4_define([b4_api_PREFIX], |
||||
|
[m4_toupper(b4_api_prefix)]) |
||||
|
|
||||
|
# b4_prefix |
||||
|
# --------- |
||||
|
# If the %name-prefix is not given, it is api.prefix. |
||||
|
m4_define_default([b4_prefix], [b4_api_prefix]) |
||||
|
|
||||
|
b4_percent_define_default([[api.parser.class]], [b4_prefix[]Parser]) |
||||
|
m4_define([b4_parser_class], [b4_percent_define_get([[api.parser.class]])]) |
||||
|
|
||||
|
b4_percent_define_default([[lex_throws]], [[java.io.IOException]]) |
||||
|
m4_define([b4_lex_throws], [b4_percent_define_get([[lex_throws]])]) |
||||
|
|
||||
|
b4_percent_define_default([[throws]], []) |
||||
|
m4_define([b4_throws], [b4_percent_define_get([[throws]])]) |
||||
|
|
||||
|
b4_percent_define_default([[init_throws]], []) |
||||
|
m4_define([b4_init_throws], [b4_percent_define_get([[init_throws]])]) |
||||
|
|
||||
|
b4_percent_define_default([[api.location.type]], [Location]) |
||||
|
m4_define([b4_location_type], [b4_percent_define_get([[api.location.type]])]) |
||||
|
|
||||
|
b4_percent_define_default([[api.position.type]], [Position]) |
||||
|
m4_define([b4_position_type], [b4_percent_define_get([[api.position.type]])]) |
||||
|
|
||||
|
|
||||
|
## ----------------- ## |
||||
|
## Semantic Values. ## |
||||
|
## ----------------- ## |
||||
|
|
||||
|
|
||||
|
# b4_symbol_translate(STRING) |
||||
|
# --------------------------- |
||||
|
# Used by "bison" in the array of symbol names to mark those that |
||||
|
# require translation. |
||||
|
m4_define([b4_symbol_translate], |
||||
|
[[i18n($1)]]) |
||||
|
|
||||
|
|
||||
|
# b4_trans(STRING) |
||||
|
# ---------------- |
||||
|
# Translate a string if i18n is enabled. Avoid collision with b4_translate. |
||||
|
m4_define([b4_trans], |
||||
|
[b4_has_translations_if([i18n($1)], [$1])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_symbol_value(VAL, [SYMBOL-NUM], [TYPE-TAG]) |
||||
|
# ---------------------------------------------- |
||||
|
# See README. |
||||
|
m4_define([b4_symbol_value], |
||||
|
[m4_ifval([$3], |
||||
|
[(($3)($1))], |
||||
|
[m4_ifval([$2], |
||||
|
[b4_symbol_if([$2], [has_type], |
||||
|
[((b4_symbol([$2], [type]))($1))], |
||||
|
[$1])], |
||||
|
[$1])])]) |
||||
|
|
||||
|
|
||||
|
# b4_lhs_value([SYMBOL-NUM], [TYPE]) |
||||
|
# ---------------------------------- |
||||
|
# See README. |
||||
|
m4_define([b4_lhs_value], [yyval]) |
||||
|
|
||||
|
|
||||
|
# b4_rhs_data(RULE-LENGTH, POS) |
||||
|
# ----------------------------- |
||||
|
# See README. |
||||
|
m4_define([b4_rhs_data], |
||||
|
[yystack.valueAt (b4_subtract($@))]) |
||||
|
|
||||
|
# b4_rhs_value(RULE-LENGTH, POS, SYMBOL-NUM, [TYPE]) |
||||
|
# -------------------------------------------------- |
||||
|
# See README. |
||||
|
# |
||||
|
# In this simple implementation, %token and %type have class names |
||||
|
# between the angle brackets. |
||||
|
m4_define([b4_rhs_value], |
||||
|
[b4_symbol_value([b4_rhs_data([$1], [$2])], [$3], [$4])]) |
||||
|
|
||||
|
|
||||
|
# b4_lhs_location() |
||||
|
# ----------------- |
||||
|
# Expansion of @$. |
||||
|
m4_define([b4_lhs_location], |
||||
|
[(yyloc)]) |
||||
|
|
||||
|
|
||||
|
# b4_rhs_location(RULE-LENGTH, POS) |
||||
|
# --------------------------------- |
||||
|
# Expansion of @POS, where the current rule has RULE-LENGTH symbols |
||||
|
# on RHS. |
||||
|
m4_define([b4_rhs_location], |
||||
|
[yystack.locationAt (b4_subtract($@))]) |
||||
|
|
||||
|
|
||||
|
# b4_lex_param |
||||
|
# b4_parse_param |
||||
|
# -------------- |
||||
|
# If defined, b4_lex_param arrives double quoted, but below we prefer |
||||
|
# it to be single quoted. Same for b4_parse_param. |
||||
|
|
||||
|
# TODO: should be in bison.m4 |
||||
|
m4_define_default([b4_lex_param], [[]]) |
||||
|
m4_define([b4_lex_param], b4_lex_param) |
||||
|
m4_define([b4_parse_param], b4_parse_param) |
||||
|
|
||||
|
# b4_lex_param_decl |
||||
|
# ----------------- |
||||
|
# Extra formal arguments of the constructor. |
||||
|
m4_define([b4_lex_param_decl], |
||||
|
[m4_ifset([b4_lex_param], |
||||
|
[b4_remove_comma([$1], |
||||
|
b4_param_decls(b4_lex_param))], |
||||
|
[$1])]) |
||||
|
|
||||
|
m4_define([b4_param_decls], |
||||
|
[m4_map([b4_param_decl], [$@])]) |
||||
|
m4_define([b4_param_decl], [, $1]) |
||||
|
|
||||
|
m4_define([b4_remove_comma], [m4_ifval(m4_quote($1), [$1, ], [])m4_shift2($@)]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_decl |
||||
|
# ------------------- |
||||
|
# Extra formal arguments of the constructor. |
||||
|
m4_define([b4_parse_param_decl], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[b4_remove_comma([$1], |
||||
|
b4_param_decls(b4_parse_param))], |
||||
|
[$1])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_lex_param_call |
||||
|
# ----------------- |
||||
|
# Delegating the lexer parameters to the lexer constructor. |
||||
|
m4_define([b4_lex_param_call], |
||||
|
[m4_ifset([b4_lex_param], |
||||
|
[b4_remove_comma([$1], |
||||
|
b4_param_calls(b4_lex_param))], |
||||
|
[$1])]) |
||||
|
m4_define([b4_param_calls], |
||||
|
[m4_map([b4_param_call], [$@])]) |
||||
|
m4_define([b4_param_call], [, $2]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_cons |
||||
|
# ------------------- |
||||
|
# Extra initialisations of the constructor. |
||||
|
m4_define([b4_parse_param_cons], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[b4_constructor_calls(b4_parse_param)])]) |
||||
|
|
||||
|
m4_define([b4_constructor_calls], |
||||
|
[m4_map([b4_constructor_call], [$@])]) |
||||
|
m4_define([b4_constructor_call], |
||||
|
[this.$2 = $2; |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_parse_param_vars |
||||
|
# ------------------- |
||||
|
# Extra instance variables. |
||||
|
m4_define([b4_parse_param_vars], |
||||
|
[m4_ifset([b4_parse_param], |
||||
|
[ |
||||
|
/* User arguments. */ |
||||
|
b4_var_decls(b4_parse_param)])]) |
||||
|
|
||||
|
m4_define([b4_var_decls], |
||||
|
[m4_map_sep([b4_var_decl], [ |
||||
|
], [$@])]) |
||||
|
m4_define([b4_var_decl], |
||||
|
[ protected final $1;]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_maybe_throws(THROWS) |
||||
|
# ----------------------- |
||||
|
# Expand to either an empty string or "throws THROWS". |
||||
|
m4_define([b4_maybe_throws], |
||||
|
[m4_ifval($1, [ throws $1])]) |
||||
1633
tools/winflexbison/data/skeletons/lalr1.cc
File diff suppressed because it is too large
View File
1326
tools/winflexbison/data/skeletons/lalr1.d
File diff suppressed because it is too large
View File
1303
tools/winflexbison/data/skeletons/lalr1.java
File diff suppressed because it is too large
View File
@ -0,0 +1,380 @@ |
|||||
|
# C++ skeleton for Bison
|
||||
|
|
||||
|
# Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
|
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify
|
||||
|
# it under the terms of the GNU General Public License as published by
|
||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||
|
# (at your option) any later version.
|
||||
|
#
|
||||
|
# This program is distributed in the hope that it will be useful,
|
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
# GNU General Public License for more details.
|
||||
|
#
|
||||
|
# You should have received a copy of the GNU General Public License
|
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
m4_pushdef([b4_copyright_years], |
||||
|
[2002-2015, 2018-2021]) |
||||
|
|
||||
|
|
||||
|
# b4_location_file
|
||||
|
# ----------------
|
||||
|
# Name of the file containing the position/location class,
|
||||
|
# if we want this file.
|
||||
|
b4_percent_define_check_file([b4_location_file], |
||||
|
[[api.location.file]], |
||||
|
b4_header_if([[location.hh]])) |
||||
|
|
||||
|
# b4_location_include
|
||||
|
# -------------------
|
||||
|
# If location.hh is to be generated, the name under which should it be
|
||||
|
# included.
|
||||
|
#
|
||||
|
# b4_location_path
|
||||
|
# ----------------
|
||||
|
# The path to use for the CPP guard.
|
||||
|
m4_ifdef([b4_location_file], |
||||
|
[m4_define([b4_location_include], |
||||
|
[b4_percent_define_get([[api.location.include]], |
||||
|
["b4_location_file"])]) |
||||
|
m4_define([b4_location_path], |
||||
|
b4_percent_define_get([[api.location.include]], |
||||
|
["b4_mapped_dir_prefix[]b4_location_file"])) |
||||
|
m4_define([b4_location_path], |
||||
|
m4_substr(m4_defn([b4_location_path]), 1, m4_eval(m4_len(m4_defn([b4_location_path])) - 2))) |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_position_file
|
||||
|
# ----------------
|
||||
|
# Name of the file containing the position class, if we want this file.
|
||||
|
b4_header_if( |
||||
|
[b4_required_version_if( |
||||
|
[30200], [], |
||||
|
[m4_ifdef([b4_location_file], |
||||
|
[m4_define([b4_position_file], [position.hh])])])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# b4_location_define
|
||||
|
# ------------------
|
||||
|
# Define the position and location classes.
|
||||
|
m4_define([b4_location_define], |
||||
|
[[ /// A point in a source file.
|
||||
|
class position |
||||
|
{ |
||||
|
public: |
||||
|
/// Type for file name.
|
||||
|
typedef ]b4_percent_define_get([[api.filename.type]])[ filename_type; |
||||
|
/// Type for line and column numbers.
|
||||
|
typedef int counter_type; |
||||
|
]m4_ifdef([b4_location_constructors], [[ |
||||
|
/// Construct a position.
|
||||
|
explicit position (filename_type* f = YY_NULLPTR, |
||||
|
counter_type l = ]b4_location_initial_line[, |
||||
|
counter_type c = ]b4_location_initial_column[) |
||||
|
: filename (f) |
||||
|
, line (l) |
||||
|
, column (c) |
||||
|
{} |
||||
|
|
||||
|
]])[ |
||||
|
/// Initialization.
|
||||
|
void initialize (filename_type* fn = YY_NULLPTR, |
||||
|
counter_type l = ]b4_location_initial_line[, |
||||
|
counter_type c = ]b4_location_initial_column[) |
||||
|
{ |
||||
|
filename = fn; |
||||
|
line = l; |
||||
|
column = c; |
||||
|
} |
||||
|
|
||||
|
/** \name Line and Column related manipulators
|
||||
|
** \{ */ |
||||
|
/// (line related) Advance to the COUNT next lines.
|
||||
|
void lines (counter_type count = 1) |
||||
|
{ |
||||
|
if (count) |
||||
|
{ |
||||
|
column = ]b4_location_initial_column[; |
||||
|
line = add_ (line, count, ]b4_location_initial_line[); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// (column related) Advance to the COUNT next columns.
|
||||
|
void columns (counter_type count = 1) |
||||
|
{ |
||||
|
column = add_ (column, count, ]b4_location_initial_column[); |
||||
|
} |
||||
|
/** \} */ |
||||
|
|
||||
|
/// File name to which this position refers.
|
||||
|
filename_type* filename; |
||||
|
/// Current line number.
|
||||
|
counter_type line; |
||||
|
/// Current column number.
|
||||
|
counter_type column; |
||||
|
|
||||
|
private: |
||||
|
/// Compute max (min, lhs+rhs).
|
||||
|
static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min) |
||||
|
{ |
||||
|
return lhs + rhs < min ? min : lhs + rhs; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/// Add \a width columns, in place.
|
||||
|
inline position& |
||||
|
operator+= (position& res, position::counter_type width) |
||||
|
{ |
||||
|
res.columns (width); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/// Add \a width columns.
|
||||
|
inline position |
||||
|
operator+ (position res, position::counter_type width) |
||||
|
{ |
||||
|
return res += width; |
||||
|
} |
||||
|
|
||||
|
/// Subtract \a width columns, in place.
|
||||
|
inline position& |
||||
|
operator-= (position& res, position::counter_type width) |
||||
|
{ |
||||
|
return res += -width; |
||||
|
} |
||||
|
|
||||
|
/// Subtract \a width columns.
|
||||
|
inline position |
||||
|
operator- (position res, position::counter_type width) |
||||
|
{ |
||||
|
return res -= width; |
||||
|
} |
||||
|
]b4_percent_define_flag_if([[define_location_comparison]], [[ |
||||
|
/// Compare two position objects.
|
||||
|
inline bool |
||||
|
operator== (const position& pos1, const position& pos2) |
||||
|
{ |
||||
|
return (pos1.line == pos2.line |
||||
|
&& pos1.column == pos2.column |
||||
|
&& (pos1.filename == pos2.filename |
||||
|
|| (pos1.filename && pos2.filename |
||||
|
&& *pos1.filename == *pos2.filename))); |
||||
|
} |
||||
|
|
||||
|
/// Compare two position objects.
|
||||
|
inline bool |
||||
|
operator!= (const position& pos1, const position& pos2) |
||||
|
{ |
||||
|
return !(pos1 == pos2); |
||||
|
} |
||||
|
]])[ |
||||
|
/** \brief Intercept output stream redirection.
|
||||
|
** \param ostr the destination output stream |
||||
|
** \param pos a reference to the position to redirect |
||||
|
*/ |
||||
|
template <typename YYChar> |
||||
|
std::basic_ostream<YYChar>& |
||||
|
operator<< (std::basic_ostream<YYChar>& ostr, const position& pos) |
||||
|
{ |
||||
|
if (pos.filename) |
||||
|
ostr << *pos.filename << ':'; |
||||
|
return ostr << pos.line << '.' << pos.column; |
||||
|
} |
||||
|
|
||||
|
/// Two points in a source file.
|
||||
|
class location |
||||
|
{ |
||||
|
public: |
||||
|
/// Type for file name.
|
||||
|
typedef position::filename_type filename_type; |
||||
|
/// Type for line and column numbers.
|
||||
|
typedef position::counter_type counter_type; |
||||
|
]m4_ifdef([b4_location_constructors], [ |
||||
|
/// Construct a location from \a b to \a e.
|
||||
|
location (const position& b, const position& e) |
||||
|
: begin (b) |
||||
|
, end (e) |
||||
|
{} |
||||
|
|
||||
|
/// Construct a 0-width location in \a p.
|
||||
|
explicit location (const position& p = position ()) |
||||
|
: begin (p) |
||||
|
, end (p) |
||||
|
{} |
||||
|
|
||||
|
/// Construct a 0-width location in \a f, \a l, \a c.
|
||||
|
explicit location (filename_type* f, |
||||
|
counter_type l = ]b4_location_initial_line[, |
||||
|
counter_type c = ]b4_location_initial_column[) |
||||
|
: begin (f, l, c) |
||||
|
, end (f, l, c) |
||||
|
{} |
||||
|
|
||||
|
])[ |
||||
|
/// Initialization.
|
||||
|
void initialize (filename_type* f = YY_NULLPTR, |
||||
|
counter_type l = ]b4_location_initial_line[, |
||||
|
counter_type c = ]b4_location_initial_column[) |
||||
|
{ |
||||
|
begin.initialize (f, l, c); |
||||
|
end = begin; |
||||
|
} |
||||
|
|
||||
|
/** \name Line and Column related manipulators
|
||||
|
** \{ */ |
||||
|
public: |
||||
|
/// Reset initial location to final location.
|
||||
|
void step () |
||||
|
{ |
||||
|
begin = end; |
||||
|
} |
||||
|
|
||||
|
/// Extend the current location to the COUNT next columns.
|
||||
|
void columns (counter_type count = 1) |
||||
|
{ |
||||
|
end += count; |
||||
|
} |
||||
|
|
||||
|
/// Extend the current location to the COUNT next lines.
|
||||
|
void lines (counter_type count = 1) |
||||
|
{ |
||||
|
end.lines (count); |
||||
|
} |
||||
|
/** \} */ |
||||
|
|
||||
|
|
||||
|
public: |
||||
|
/// Beginning of the located region.
|
||||
|
position begin; |
||||
|
/// End of the located region.
|
||||
|
position end; |
||||
|
}; |
||||
|
|
||||
|
/// Join two locations, in place.
|
||||
|
inline location& |
||||
|
operator+= (location& res, const location& end) |
||||
|
{ |
||||
|
res.end = end.end; |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/// Join two locations.
|
||||
|
inline location |
||||
|
operator+ (location res, const location& end) |
||||
|
{ |
||||
|
return res += end; |
||||
|
} |
||||
|
|
||||
|
/// Add \a width columns to the end position, in place.
|
||||
|
inline location& |
||||
|
operator+= (location& res, location::counter_type width) |
||||
|
{ |
||||
|
res.columns (width); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/// Add \a width columns to the end position.
|
||||
|
inline location |
||||
|
operator+ (location res, location::counter_type width) |
||||
|
{ |
||||
|
return res += width; |
||||
|
} |
||||
|
|
||||
|
/// Subtract \a width columns to the end position, in place.
|
||||
|
inline location& |
||||
|
operator-= (location& res, location::counter_type width) |
||||
|
{ |
||||
|
return res += -width; |
||||
|
} |
||||
|
|
||||
|
/// Subtract \a width columns to the end position.
|
||||
|
inline location |
||||
|
operator- (location res, location::counter_type width) |
||||
|
{ |
||||
|
return res -= width; |
||||
|
} |
||||
|
]b4_percent_define_flag_if([[define_location_comparison]], [[ |
||||
|
/// Compare two location objects.
|
||||
|
inline bool |
||||
|
operator== (const location& loc1, const location& loc2) |
||||
|
{ |
||||
|
return loc1.begin == loc2.begin && loc1.end == loc2.end; |
||||
|
} |
||||
|
|
||||
|
/// Compare two location objects.
|
||||
|
inline bool |
||||
|
operator!= (const location& loc1, const location& loc2) |
||||
|
{ |
||||
|
return !(loc1 == loc2); |
||||
|
} |
||||
|
]])[ |
||||
|
/** \brief Intercept output stream redirection.
|
||||
|
** \param ostr the destination output stream |
||||
|
** \param loc a reference to the location to redirect |
||||
|
** |
||||
|
** Avoid duplicate information. |
||||
|
*/ |
||||
|
template <typename YYChar> |
||||
|
std::basic_ostream<YYChar>& |
||||
|
operator<< (std::basic_ostream<YYChar>& ostr, const location& loc) |
||||
|
{ |
||||
|
location::counter_type end_col |
||||
|
= 0 < loc.end.column ? loc.end.column - 1 : 0; |
||||
|
ostr << loc.begin; |
||||
|
if (loc.end.filename |
||||
|
&& (!loc.begin.filename |
||||
|
|| *loc.begin.filename != *loc.end.filename)) |
||||
|
ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col; |
||||
|
else if (loc.begin.line < loc.end.line) |
||||
|
ostr << '-' << loc.end.line << '.' << end_col; |
||||
|
else if (loc.begin.column < end_col) |
||||
|
ostr << '-' << end_col; |
||||
|
return ostr; |
||||
|
} |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
m4_ifdef([b4_position_file], [[ |
||||
|
]b4_output_begin([b4_dir_prefix], [b4_position_file])[ |
||||
|
]b4_generated_by[ |
||||
|
// Starting with Bison 3.2, this file is useless: the structure it
|
||||
|
// used to define is now defined in "]b4_location_file[".
|
||||
|
//
|
||||
|
// To get rid of this file:
|
||||
|
// 1. add '%require "3.2"' (or newer) to your grammar file
|
||||
|
// 2. remove references to this file from your build system
|
||||
|
// 3. if you used to include it, include "]b4_location_file[" instead.
|
||||
|
|
||||
|
#include ]b4_location_include[
|
||||
|
]b4_output_end[ |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
m4_ifdef([b4_location_file], [[ |
||||
|
]b4_output_begin([b4_dir_prefix], [b4_location_file])[ |
||||
|
]b4_copyright([Locations for Bison parsers in C++])[ |
||||
|
/**
|
||||
|
** \file ]b4_location_path[ |
||||
|
** Define the ]b4_namespace_ref[::location class. |
||||
|
*/ |
||||
|
|
||||
|
]b4_cpp_guard_open([b4_location_path])[ |
||||
|
|
||||
|
# include <iostream>
|
||||
|
# include <string>
|
||||
|
|
||||
|
]b4_null_define[ |
||||
|
|
||||
|
]b4_namespace_open[ |
||||
|
]b4_location_define[ |
||||
|
]b4_namespace_close[ |
||||
|
]b4_cpp_guard_close([b4_location_path])[ |
||||
|
]b4_output_end[ |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
m4_popdef([b4_copyright_years]) |
||||
@ -0,0 +1,157 @@ |
|||||
|
# C++ skeleton for Bison
|
||||
|
|
||||
|
# Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
|
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify
|
||||
|
# it under the terms of the GNU General Public License as published by
|
||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||
|
# (at your option) any later version.
|
||||
|
#
|
||||
|
# This program is distributed in the hope that it will be useful,
|
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
# GNU General Public License for more details.
|
||||
|
#
|
||||
|
# You should have received a copy of the GNU General Public License
|
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
|
||||
|
# b4_stack_file
|
||||
|
# -------------
|
||||
|
# Name of the file containing the stack class, if we want this file.
|
||||
|
b4_header_if([b4_required_version_if([30200], [], |
||||
|
[m4_define([b4_stack_file], [stack.hh])])]) |
||||
|
|
||||
|
|
||||
|
# b4_stack_define
|
||||
|
# ---------------
|
||||
|
m4_define([b4_stack_define], |
||||
|
[[ /// A stack with random access from its top.
|
||||
|
template <typename T, typename S = std::vector<T> > |
||||
|
class stack |
||||
|
{ |
||||
|
public: |
||||
|
// Hide our reversed order.
|
||||
|
typedef typename S::iterator iterator; |
||||
|
typedef typename S::const_iterator const_iterator; |
||||
|
typedef typename S::size_type size_type; |
||||
|
typedef typename std::ptrdiff_t index_type; |
||||
|
|
||||
|
stack (size_type n = 200) YY_NOEXCEPT |
||||
|
: seq_ (n) |
||||
|
{} |
||||
|
|
||||
|
#if 201103L <= YY_CPLUSPLUS
|
||||
|
/// Non copyable.
|
||||
|
stack (const stack&) = delete; |
||||
|
/// Non copyable.
|
||||
|
stack& operator= (const stack&) = delete; |
||||
|
#endif
|
||||
|
|
||||
|
/// Random access.
|
||||
|
///
|
||||
|
/// Index 0 returns the topmost element.
|
||||
|
const T& |
||||
|
operator[] (index_type i) const |
||||
|
{ |
||||
|
return seq_[size_type (size () - 1 - i)]; |
||||
|
} |
||||
|
|
||||
|
/// Random access.
|
||||
|
///
|
||||
|
/// Index 0 returns the topmost element.
|
||||
|
T& |
||||
|
operator[] (index_type i) |
||||
|
{ |
||||
|
return seq_[size_type (size () - 1 - i)]; |
||||
|
} |
||||
|
|
||||
|
/// Steal the contents of \a t.
|
||||
|
///
|
||||
|
/// Close to move-semantics.
|
||||
|
void |
||||
|
push (YY_MOVE_REF (T) t) |
||||
|
{ |
||||
|
seq_.push_back (T ()); |
||||
|
operator[] (0).move (t); |
||||
|
} |
||||
|
|
||||
|
/// Pop elements from the stack.
|
||||
|
void |
||||
|
pop (std::ptrdiff_t n = 1) YY_NOEXCEPT |
||||
|
{ |
||||
|
for (; 0 < n; --n) |
||||
|
seq_.pop_back (); |
||||
|
} |
||||
|
|
||||
|
/// Pop all elements from the stack.
|
||||
|
void |
||||
|
clear () YY_NOEXCEPT |
||||
|
{ |
||||
|
seq_.clear (); |
||||
|
} |
||||
|
|
||||
|
/// Number of elements on the stack.
|
||||
|
index_type |
||||
|
size () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return index_type (seq_.size ()); |
||||
|
} |
||||
|
|
||||
|
/// Iterator on top of the stack (going downwards).
|
||||
|
const_iterator |
||||
|
begin () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return seq_.begin (); |
||||
|
} |
||||
|
|
||||
|
/// Bottom of the stack.
|
||||
|
const_iterator |
||||
|
end () const YY_NOEXCEPT |
||||
|
{ |
||||
|
return seq_.end (); |
||||
|
} |
||||
|
|
||||
|
/// Present a slice of the top of a stack.
|
||||
|
class slice |
||||
|
{ |
||||
|
public: |
||||
|
slice (const stack& stack, index_type range) YY_NOEXCEPT |
||||
|
: stack_ (stack) |
||||
|
, range_ (range) |
||||
|
{} |
||||
|
|
||||
|
const T& |
||||
|
operator[] (index_type i) const |
||||
|
{ |
||||
|
return stack_[range_ - i]; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const stack& stack_; |
||||
|
index_type range_; |
||||
|
}; |
||||
|
|
||||
|
private: |
||||
|
#if YY_CPLUSPLUS < 201103L
|
||||
|
/// Non copyable.
|
||||
|
stack (const stack&); |
||||
|
/// Non copyable.
|
||||
|
stack& operator= (const stack&); |
||||
|
#endif
|
||||
|
/// The wrapped container.
|
||||
|
S seq_; |
||||
|
}; |
||||
|
]]) |
||||
|
|
||||
|
m4_ifdef([b4_stack_file], |
||||
|
[b4_output_begin([b4_dir_prefix], [b4_stack_file])[ |
||||
|
]b4_generated_by[ |
||||
|
// Starting with Bison 3.2, this file is useless: the structure it
|
||||
|
// used to define is now defined with the parser itself.
|
||||
|
//
|
||||
|
// To get rid of this file:
|
||||
|
// 1. add '%require "3.2"' (or newer) to your grammar file
|
||||
|
// 2. remove references to this file from your build system.
|
||||
|
]b4_output_end[ |
||||
|
]]) |
||||
@ -0,0 +1,2 @@ |
|||||
|
dnl GNU M4 treats -dV in a position-independent manner. |
||||
|
m4_debugmode(V)m4_traceon()dnl |
||||
@ -0,0 +1,525 @@ |
|||||
|
# C++ skeleton for Bison
|
||||
|
|
||||
|
# Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
|
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify
|
||||
|
# it under the terms of the GNU General Public License as published by
|
||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||
|
# (at your option) any later version.
|
||||
|
#
|
||||
|
# This program is distributed in the hope that it will be useful,
|
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
# GNU General Public License for more details.
|
||||
|
#
|
||||
|
# You should have received a copy of the GNU General Public License
|
||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
|
||||
|
## --------- ##
|
||||
|
## variant. ##
|
||||
|
## --------- ##
|
||||
|
|
||||
|
# b4_assert
|
||||
|
# ---------
|
||||
|
# The name of YY_ASSERT.
|
||||
|
m4_define([b4_assert], |
||||
|
[b4_api_PREFIX[]_ASSERT]) |
||||
|
|
||||
|
|
||||
|
# b4_symbol_variant(YYTYPE, YYVAL, ACTION, [ARGS])
|
||||
|
# ------------------------------------------------
|
||||
|
# Run some ACTION ("build", or "destroy") on YYVAL of symbol type
|
||||
|
# YYTYPE.
|
||||
|
m4_define([b4_symbol_variant], |
||||
|
[m4_pushdef([b4_dollar_dollar], |
||||
|
[$2.$3< $][3 > (m4_shift3($@))])dnl |
||||
|
switch ($1) |
||||
|
{ |
||||
|
b4_type_foreach([_b4_type_action])[]dnl |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
m4_popdef([b4_dollar_dollar])dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# _b4_char_sizeof_counter
|
||||
|
# -----------------------
|
||||
|
# A counter used by _b4_char_sizeof_dummy to create fresh symbols.
|
||||
|
m4_define([_b4_char_sizeof_counter], |
||||
|
[0]) |
||||
|
|
||||
|
# _b4_char_sizeof_dummy
|
||||
|
# ---------------------
|
||||
|
# At each call return a new C++ identifier.
|
||||
|
m4_define([_b4_char_sizeof_dummy], |
||||
|
[m4_define([_b4_char_sizeof_counter], m4_incr(_b4_char_sizeof_counter))dnl |
||||
|
dummy[]_b4_char_sizeof_counter]) |
||||
|
|
||||
|
|
||||
|
# b4_char_sizeof(SYMBOL-NUMS)
|
||||
|
# ---------------------------
|
||||
|
# To be mapped on the list of type names to produce:
|
||||
|
#
|
||||
|
# char dummy1[sizeof (type_name_1)];
|
||||
|
# char dummy2[sizeof (type_name_2)];
|
||||
|
#
|
||||
|
# for defined type names.
|
||||
|
m4_define([b4_char_sizeof], |
||||
|
[b4_symbol_if([$1], [has_type], |
||||
|
[ |
||||
|
m4_map([ b4_symbol_tag_comment], [$@])dnl |
||||
|
char _b4_char_sizeof_dummy@{sizeof (b4_symbol([$1], [type]))@}; |
||||
|
])]) |
||||
|
|
||||
|
|
||||
|
# b4_variant_includes
|
||||
|
# -------------------
|
||||
|
# The needed includes for variants support.
|
||||
|
m4_define([b4_variant_includes], |
||||
|
[b4_parse_assert_if([[#include <typeinfo> |
||||
|
#ifndef ]b4_assert[
|
||||
|
# include <cassert>
|
||||
|
# define ]b4_assert[ assert
|
||||
|
#endif
|
||||
|
]])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## -------------------------- ##
|
||||
|
## Adjustments for variants. ##
|
||||
|
## -------------------------- ##
|
||||
|
|
||||
|
|
||||
|
# b4_value_type_declare
|
||||
|
# ---------------------
|
||||
|
# Define value_type.
|
||||
|
m4_define([b4_value_type_declare], |
||||
|
[[ /// A buffer to store and retrieve objects.
|
||||
|
///
|
||||
|
/// Sort of a variant, but does not keep track of the nature
|
||||
|
/// of the stored data, since that knowledge is available
|
||||
|
/// via the current parser state.
|
||||
|
class value_type |
||||
|
{ |
||||
|
public: |
||||
|
/// Type of *this.
|
||||
|
typedef value_type self_type; |
||||
|
|
||||
|
/// Empty construction.
|
||||
|
value_type () YY_NOEXCEPT |
||||
|
: yyraw_ ()]b4_parse_assert_if([ |
||||
|
, yytypeid_ (YY_NULLPTR)])[ |
||||
|
{} |
||||
|
|
||||
|
/// Construct and fill.
|
||||
|
template <typename T> |
||||
|
value_type (YY_RVREF (T) t)]b4_parse_assert_if([ |
||||
|
: yytypeid_ (&typeid (T))])[ |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (sizeof (T) <= size);]])[ |
||||
|
new (yyas_<T> ()) T (YY_MOVE (t)); |
||||
|
} |
||||
|
|
||||
|
#if 201103L <= YY_CPLUSPLUS
|
||||
|
/// Non copyable.
|
||||
|
value_type (const self_type&) = delete; |
||||
|
/// Non copyable.
|
||||
|
self_type& operator= (const self_type&) = delete; |
||||
|
#endif
|
||||
|
|
||||
|
/// Destruction, allowed only if empty.
|
||||
|
~value_type () YY_NOEXCEPT |
||||
|
{]b4_parse_assert_if([ |
||||
|
]b4_assert[ (!yytypeid_); |
||||
|
])[} |
||||
|
|
||||
|
# if 201103L <= YY_CPLUSPLUS
|
||||
|
/// Instantiate a \a T in here from \a t.
|
||||
|
template <typename T, typename... U> |
||||
|
T& |
||||
|
emplace (U&&... u) |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (!yytypeid_); |
||||
|
]b4_assert[ (sizeof (T) <= size); |
||||
|
yytypeid_ = & typeid (T);]])[ |
||||
|
return *new (yyas_<T> ()) T (std::forward <U>(u)...); |
||||
|
} |
||||
|
# else
|
||||
|
/// Instantiate an empty \a T in here.
|
||||
|
template <typename T> |
||||
|
T& |
||||
|
emplace () |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (!yytypeid_); |
||||
|
]b4_assert[ (sizeof (T) <= size); |
||||
|
yytypeid_ = & typeid (T);]])[ |
||||
|
return *new (yyas_<T> ()) T (); |
||||
|
} |
||||
|
|
||||
|
/// Instantiate a \a T in here from \a t.
|
||||
|
template <typename T> |
||||
|
T& |
||||
|
emplace (const T& t) |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (!yytypeid_); |
||||
|
]b4_assert[ (sizeof (T) <= size); |
||||
|
yytypeid_ = & typeid (T);]])[ |
||||
|
return *new (yyas_<T> ()) T (t); |
||||
|
} |
||||
|
# endif
|
||||
|
|
||||
|
/// Instantiate an empty \a T in here.
|
||||
|
/// Obsolete, use emplace.
|
||||
|
template <typename T> |
||||
|
T& |
||||
|
build () |
||||
|
{ |
||||
|
return emplace<T> (); |
||||
|
} |
||||
|
|
||||
|
/// Instantiate a \a T in here from \a t.
|
||||
|
/// Obsolete, use emplace.
|
||||
|
template <typename T> |
||||
|
T& |
||||
|
build (const T& t) |
||||
|
{ |
||||
|
return emplace<T> (t); |
||||
|
} |
||||
|
|
||||
|
/// Accessor to a built \a T.
|
||||
|
template <typename T> |
||||
|
T& |
||||
|
as () YY_NOEXCEPT |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (yytypeid_); |
||||
|
]b4_assert[ (*yytypeid_ == typeid (T)); |
||||
|
]b4_assert[ (sizeof (T) <= size);]])[ |
||||
|
return *yyas_<T> (); |
||||
|
} |
||||
|
|
||||
|
/// Const accessor to a built \a T (for %printer).
|
||||
|
template <typename T> |
||||
|
const T& |
||||
|
as () const YY_NOEXCEPT |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (yytypeid_); |
||||
|
]b4_assert[ (*yytypeid_ == typeid (T)); |
||||
|
]b4_assert[ (sizeof (T) <= size);]])[ |
||||
|
return *yyas_<T> (); |
||||
|
} |
||||
|
|
||||
|
/// Swap the content with \a that, of same type.
|
||||
|
///
|
||||
|
/// Both variants must be built beforehand, because swapping the actual
|
||||
|
/// data requires reading it (with as()), and this is not possible on
|
||||
|
/// unconstructed variants: it would require some dynamic testing, which
|
||||
|
/// should not be the variant's responsibility.
|
||||
|
/// Swapping between built and (possibly) non-built is done with
|
||||
|
/// self_type::move ().
|
||||
|
template <typename T> |
||||
|
void |
||||
|
swap (self_type& that) YY_NOEXCEPT |
||||
|
{]b4_parse_assert_if([[ |
||||
|
]b4_assert[ (yytypeid_); |
||||
|
]b4_assert[ (*yytypeid_ == *that.yytypeid_);]])[ |
||||
|
std::swap (as<T> (), that.as<T> ()); |
||||
|
} |
||||
|
|
||||
|
/// Move the content of \a that to this.
|
||||
|
///
|
||||
|
/// Destroys \a that.
|
||||
|
template <typename T> |
||||
|
void |
||||
|
move (self_type& that) |
||||
|
{ |
||||
|
# if 201103L <= YY_CPLUSPLUS
|
||||
|
emplace<T> (std::move (that.as<T> ())); |
||||
|
# else
|
||||
|
emplace<T> (); |
||||
|
swap<T> (that); |
||||
|
# endif
|
||||
|
that.destroy<T> (); |
||||
|
} |
||||
|
|
||||
|
# if 201103L <= YY_CPLUSPLUS
|
||||
|
/// Move the content of \a that to this.
|
||||
|
template <typename T> |
||||
|
void |
||||
|
move (self_type&& that) |
||||
|
{ |
||||
|
emplace<T> (std::move (that.as<T> ())); |
||||
|
that.destroy<T> (); |
||||
|
} |
||||
|
#endif
|
||||
|
|
||||
|
/// Copy the content of \a that to this.
|
||||
|
template <typename T> |
||||
|
void |
||||
|
copy (const self_type& that) |
||||
|
{ |
||||
|
emplace<T> (that.as<T> ()); |
||||
|
} |
||||
|
|
||||
|
/// Destroy the stored \a T.
|
||||
|
template <typename T> |
||||
|
void |
||||
|
destroy () |
||||
|
{ |
||||
|
as<T> ().~T ();]b4_parse_assert_if([ |
||||
|
yytypeid_ = YY_NULLPTR;])[ |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
#if YY_CPLUSPLUS < 201103L
|
||||
|
/// Non copyable.
|
||||
|
value_type (const self_type&); |
||||
|
/// Non copyable.
|
||||
|
self_type& operator= (const self_type&); |
||||
|
#endif
|
||||
|
|
||||
|
/// Accessor to raw memory as \a T.
|
||||
|
template <typename T> |
||||
|
T* |
||||
|
yyas_ () YY_NOEXCEPT |
||||
|
{ |
||||
|
void *yyp = yyraw_; |
||||
|
return static_cast<T*> (yyp); |
||||
|
} |
||||
|
|
||||
|
/// Const accessor to raw memory as \a T.
|
||||
|
template <typename T> |
||||
|
const T* |
||||
|
yyas_ () const YY_NOEXCEPT |
||||
|
{ |
||||
|
const void *yyp = yyraw_; |
||||
|
return static_cast<const T*> (yyp); |
||||
|
} |
||||
|
|
||||
|
/// An auxiliary type to compute the largest semantic type.
|
||||
|
union union_type |
||||
|
{]b4_type_foreach([b4_char_sizeof])[ }; |
||||
|
|
||||
|
/// The size of the largest semantic type.
|
||||
|
enum { size = sizeof (union_type) }; |
||||
|
|
||||
|
/// A buffer to store semantic values.
|
||||
|
union |
||||
|
{ |
||||
|
/// Strongest alignment constraints.
|
||||
|
long double yyalign_me_; |
||||
|
/// A buffer large enough to store any of the semantic values.
|
||||
|
char yyraw_[size]; |
||||
|
};]b4_parse_assert_if([ |
||||
|
|
||||
|
/// Whether the content is built: if defined, the name of the stored type.
|
||||
|
const std::type_info *yytypeid_;])[ |
||||
|
}; |
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# How the semantic value is extracted when using variants.
|
||||
|
|
||||
|
# b4_symbol_value(VAL, SYMBOL-NUM, [TYPE])
|
||||
|
# ----------------------------------------
|
||||
|
# See README.
|
||||
|
m4_define([b4_symbol_value], |
||||
|
[m4_ifval([$3], |
||||
|
[$1.as< $3 > ()], |
||||
|
[m4_ifval([$2], |
||||
|
[b4_symbol_if([$2], [has_type], |
||||
|
[$1.as < b4_symbol([$2], [type]) > ()], |
||||
|
[$1])], |
||||
|
[$1])])]) |
||||
|
|
||||
|
# b4_symbol_value_template(VAL, SYMBOL-NUM, [TYPE])
|
||||
|
# -------------------------------------------------
|
||||
|
# Same as b4_symbol_value, but used in a template method.
|
||||
|
m4_define([b4_symbol_value_template], |
||||
|
[m4_ifval([$3], |
||||
|
[$1.template as< $3 > ()], |
||||
|
[m4_ifval([$2], |
||||
|
[b4_symbol_if([$2], [has_type], |
||||
|
[$1.template as < b4_symbol([$2], [type]) > ()], |
||||
|
[$1])], |
||||
|
[$1])])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
## ------------- ##
|
||||
|
## make_SYMBOL. ##
|
||||
|
## ------------- ##
|
||||
|
|
||||
|
|
||||
|
# _b4_includes_tokens(SYMBOL-NUM...)
|
||||
|
# ----------------------------------
|
||||
|
# Expands to non-empty iff one of the SYMBOL-NUM denotes
|
||||
|
# a token.
|
||||
|
m4_define([_b4_is_token], |
||||
|
[b4_symbol_if([$1], [is_token], [1])]) |
||||
|
m4_define([_b4_includes_tokens], |
||||
|
[m4_map([_b4_is_token], [$@])]) |
||||
|
|
||||
|
|
||||
|
# _b4_token_maker_define(SYMBOL-NUM)
|
||||
|
# ----------------------------------
|
||||
|
# Declare make_SYMBOL for SYMBOL-NUM. Use at class-level.
|
||||
|
m4_define([_b4_token_maker_define], |
||||
|
[b4_token_visible_if([$1], |
||||
|
[#if 201103L <= YY_CPLUSPLUS |
||||
|
static |
||||
|
symbol_type |
||||
|
make_[]_b4_symbol([$1], [id]) (b4_join( |
||||
|
b4_symbol_if([$1], [has_type], |
||||
|
[b4_symbol([$1], [type]) v]), |
||||
|
b4_locations_if([location_type l]))) |
||||
|
{ |
||||
|
return symbol_type (b4_join([token::b4_symbol([$1], [id])], |
||||
|
b4_symbol_if([$1], [has_type], [std::move (v)]), |
||||
|
b4_locations_if([std::move (l)]))); |
||||
|
} |
||||
|
#else
|
||||
|
static |
||||
|
symbol_type |
||||
|
make_[]_b4_symbol([$1], [id]) (b4_join( |
||||
|
b4_symbol_if([$1], [has_type], |
||||
|
[const b4_symbol([$1], [type])& v]), |
||||
|
b4_locations_if([const location_type& l]))) |
||||
|
{ |
||||
|
return symbol_type (b4_join([token::b4_symbol([$1], [id])], |
||||
|
b4_symbol_if([$1], [has_type], [v]), |
||||
|
b4_locations_if([l]))); |
||||
|
} |
||||
|
#endif
|
||||
|
])]) |
||||
|
|
||||
|
|
||||
|
# b4_token_kind(SYMBOL-NUM)
|
||||
|
# -------------------------
|
||||
|
# Some tokens don't have an ID.
|
||||
|
m4_define([b4_token_kind], |
||||
|
[b4_symbol_if([$1], [has_id], |
||||
|
[token::b4_symbol([$1], [id])], |
||||
|
[b4_symbol([$1], [code])])]) |
||||
|
|
||||
|
|
||||
|
# _b4_tok_in(SYMBOL-NUM, ...)
|
||||
|
# ---------------------------
|
||||
|
# See b4_tok_in below. The SYMBOL-NUMs... are tokens only.
|
||||
|
#
|
||||
|
# We iterate over the tokens to group them by "range" of token numbers (not
|
||||
|
# symbols numbers!).
|
||||
|
#
|
||||
|
# b4_fst is the start of that range.
|
||||
|
# b4_prev is the previous value.
|
||||
|
# b4_val is the current value.
|
||||
|
# If b4_val is the successor of b4_prev in token numbers, update the latter,
|
||||
|
# otherwise emit the code for range b4_fst .. b4_prev.
|
||||
|
# $1 is also used as a terminator in the foreach, but it will not be printed.
|
||||
|
#
|
||||
|
m4_define([_b4_tok_in], |
||||
|
[m4_pushdef([b4_prev], [$1])dnl |
||||
|
m4_pushdef([b4_fst], [$1])dnl |
||||
|
m4_pushdef([b4_sep], [])dnl |
||||
|
m4_foreach([b4_val], m4_dquote(m4_shift($@, $1)), |
||||
|
[m4_if(b4_symbol(b4_val, [code]), m4_eval(b4_symbol(b4_prev, [code]) + 1), [], |
||||
|
[b4_sep[]m4_if(b4_fst, b4_prev, |
||||
|
[tok == b4_token_kind(b4_fst)], |
||||
|
[(b4_token_kind(b4_fst) <= tok && tok <= b4_token_kind(b4_prev))])[]dnl |
||||
|
m4_define([b4_fst], b4_val)dnl |
||||
|
m4_define([b4_sep], [ |
||||
|
|| ])])dnl |
||||
|
m4_define([b4_prev], b4_val)])dnl |
||||
|
m4_popdef([b4_sep])dnl |
||||
|
m4_popdef([b4_fst])dnl |
||||
|
m4_popdef([b4_prev])dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# _b4_filter_tokens(SYMBOL-NUM, ...)
|
||||
|
# ----------------------------------
|
||||
|
# Expand as the list of tokens amongst SYMBOL-NUM.
|
||||
|
m4_define([_b4_filter_tokens], |
||||
|
[m4_pushdef([b4_sep])dnl |
||||
|
m4_foreach([b4_val], [$@], |
||||
|
[b4_symbol_if(b4_val, [is_token], [b4_sep[]b4_val[]m4_define([b4_sep], [,])])])dnl |
||||
|
m4_popdef([b4_sep])dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# b4_tok_in(SYMBOL-NUM, ...)
|
||||
|
# ---------------------------
|
||||
|
# A C++ conditional that checks that `tok` is a member of this list of symbol
|
||||
|
# numbers.
|
||||
|
m4_define([b4_tok_in], |
||||
|
[_$0(_b4_filter_tokens($@))]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
# _b4_symbol_constructor_define(SYMBOL-NUM...)
|
||||
|
# --------------------------------------------
|
||||
|
# Define a symbol_type constructor common to all the SYMBOL-NUM (they
|
||||
|
# have the same type). Use at class-level.
|
||||
|
m4_define([_b4_symbol_constructor_define], |
||||
|
[m4_ifval(_b4_includes_tokens($@), |
||||
|
[[#if 201103L <= YY_CPLUSPLUS |
||||
|
symbol_type (]b4_join( |
||||
|
[int tok], |
||||
|
b4_symbol_if([$1], [has_type], |
||||
|
[b4_symbol([$1], [type]) v]), |
||||
|
b4_locations_if([location_type l]))[) |
||||
|
: super_type (]b4_join([token_kind_type (tok)], |
||||
|
b4_symbol_if([$1], [has_type], [std::move (v)]), |
||||
|
b4_locations_if([std::move (l)]))[) |
||||
|
#else
|
||||
|
symbol_type (]b4_join( |
||||
|
[int tok], |
||||
|
b4_symbol_if([$1], [has_type], |
||||
|
[const b4_symbol([$1], [type])& v]), |
||||
|
b4_locations_if([const location_type& l]))[) |
||||
|
: super_type (]b4_join([token_kind_type (tok)], |
||||
|
b4_symbol_if([$1], [has_type], [v]), |
||||
|
b4_locations_if([l]))[) |
||||
|
#endif
|
||||
|
{]b4_parse_assert_if([[ |
||||
|
#if !defined _MSC_VER || defined __clang__
|
||||
|
]b4_assert[ (]b4_tok_in($@)[); |
||||
|
#endif
|
||||
|
]])[} |
||||
|
]])]) |
||||
|
|
||||
|
|
||||
|
# b4_basic_symbol_constructor_define(SYMBOL-NUM)
|
||||
|
# ----------------------------------------------
|
||||
|
# Generate a constructor for basic_symbol from given type.
|
||||
|
m4_define([b4_basic_symbol_constructor_define], |
||||
|
[[#if 201103L <= YY_CPLUSPLUS |
||||
|
basic_symbol (]b4_join( |
||||
|
[typename Base::kind_type t], |
||||
|
b4_symbol_if([$1], [has_type], [b4_symbol([$1], [type])&& v]), |
||||
|
b4_locations_if([location_type&& l]))[) |
||||
|
: Base (t)]b4_symbol_if([$1], [has_type], [ |
||||
|
, value (std::move (v))])[]b4_locations_if([ |
||||
|
, location (std::move (l))])[ |
||||
|
{} |
||||
|
#else
|
||||
|
basic_symbol (]b4_join( |
||||
|
[typename Base::kind_type t], |
||||
|
b4_symbol_if([$1], [has_type], [const b4_symbol([$1], [type])& v]), |
||||
|
b4_locations_if([const location_type& l]))[) |
||||
|
: Base (t)]b4_symbol_if([$1], [has_type], [ |
||||
|
, value (v)])[]b4_locations_if([ |
||||
|
, location (l)])[ |
||||
|
{} |
||||
|
#endif
|
||||
|
]]) |
||||
|
|
||||
|
|
||||
|
# b4_token_constructor_define
|
||||
|
# ---------------------------
|
||||
|
# Define the overloaded versions of make_FOO for all the token kinds.
|
||||
|
m4_define([b4_token_constructor_define], |
||||
|
[ // Implementation of make_symbol for each token kind.
|
||||
|
b4_symbol_foreach([_b4_token_maker_define])]) |
||||
2209
tools/winflexbison/data/skeletons/yacc.c
File diff suppressed because it is too large
View File
@ -0,0 +1,105 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<!-- |
||||
|
bison.xsl - common templates for Bison XSLT. |
||||
|
|
||||
|
Copyright (C) 2007-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
This file is part of Bison, the GNU Compiler Compiler. |
||||
|
|
||||
|
This program is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
--> |
||||
|
|
||||
|
<xsl:stylesheet version="1.0" |
||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
||||
|
xmlns:bison="https://www.gnu.org/software/bison/"> |
||||
|
|
||||
|
<xsl:key |
||||
|
name="bison:symbolByName" |
||||
|
match="/bison-xml-report/grammar/nonterminals/nonterminal" |
||||
|
use="@name" |
||||
|
/> |
||||
|
<xsl:key |
||||
|
name="bison:symbolByName" |
||||
|
match="/bison-xml-report/grammar/terminals/terminal" |
||||
|
use="@name" |
||||
|
/> |
||||
|
<xsl:key |
||||
|
name="bison:ruleByNumber" |
||||
|
match="/bison-xml-report/grammar/rules/rule" |
||||
|
use="@number" |
||||
|
/> |
||||
|
<xsl:key |
||||
|
name="bison:ruleByLhs" |
||||
|
match="/bison-xml-report/grammar/rules/rule[ |
||||
|
@usefulness != 'useless-in-grammar']" |
||||
|
use="lhs" |
||||
|
/> |
||||
|
<xsl:key |
||||
|
name="bison:ruleByRhs" |
||||
|
match="/bison-xml-report/grammar/rules/rule[ |
||||
|
@usefulness != 'useless-in-grammar']" |
||||
|
use="rhs/symbol" |
||||
|
/> |
||||
|
|
||||
|
<!-- For the specified state, output: #sr-conflicts,#rr-conflicts --> |
||||
|
<xsl:template match="state" mode="bison:count-conflicts"> |
||||
|
<xsl:variable name="transitions" select="actions/transitions"/> |
||||
|
<xsl:variable name="reductions" select="actions/reductions"/> |
||||
|
<xsl:variable |
||||
|
name="terminals" |
||||
|
select=" |
||||
|
$transitions/transition[@type='shift']/@symbol |
||||
|
| $reductions/reduction/@symbol |
||||
|
" |
||||
|
/> |
||||
|
<xsl:variable name="conflict-data"> |
||||
|
<xsl:for-each select="$terminals"> |
||||
|
<xsl:variable name="name" select="."/> |
||||
|
<xsl:if test="generate-id($terminals[. = $name][1]) = generate-id(.)"> |
||||
|
<xsl:variable |
||||
|
name="shift-count" |
||||
|
select="count($transitions/transition[@symbol=$name])" |
||||
|
/> |
||||
|
<xsl:variable |
||||
|
name="reduce-count" |
||||
|
select="count($reductions/reduction[@symbol=$name])" |
||||
|
/> |
||||
|
<xsl:if test="$shift-count > 0 and $reduce-count > 0"> |
||||
|
<xsl:text>s</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:if test="$reduce-count > 1"> |
||||
|
<xsl:text>r</xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
</xsl:variable> |
||||
|
<xsl:value-of select="string-length(translate($conflict-data, 'r', ''))"/> |
||||
|
<xsl:text>,</xsl:text> |
||||
|
<xsl:value-of select="string-length(translate($conflict-data, 's', ''))"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="space"> |
||||
|
<xsl:param name="repeat">0</xsl:param> |
||||
|
<xsl:param name="fill" select="' '"/> |
||||
|
<xsl:if test="number($repeat) >= 1"> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$repeat - 1"/> |
||||
|
<xsl:with-param name="fill" select="$fill"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:value-of select="$fill"/> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
</xsl:stylesheet> |
||||
@ -0,0 +1,401 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<!-- |
||||
|
xml2dot.xsl - transform Bison XML Report into DOT. |
||||
|
|
||||
|
Copyright (C) 2007-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
This file is part of Bison, the GNU Compiler Compiler. |
||||
|
|
||||
|
This program is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Written by Wojciech Polak <polak@gnu.org>. |
||||
|
--> |
||||
|
|
||||
|
<xsl:stylesheet version="1.0" |
||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
||||
|
xmlns:bison="https://www.gnu.org/software/bison/"> |
||||
|
|
||||
|
<xsl:import href="bison.xsl"/> |
||||
|
<xsl:output method="text" encoding="UTF-8" indent="no"/> |
||||
|
|
||||
|
<xsl:template match="/"> |
||||
|
<xsl:apply-templates select="bison-xml-report"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="bison-xml-report"> |
||||
|
<xsl:text>// Generated by GNU Bison </xsl:text> |
||||
|
<xsl:value-of select="@version"/> |
||||
|
<xsl:text>. </xsl:text> |
||||
|
<xsl:text>// Report bugs to <</xsl:text> |
||||
|
<xsl:value-of select="@bug-report"/> |
||||
|
<xsl:text>>. </xsl:text> |
||||
|
<xsl:text>// Home page: <</xsl:text> |
||||
|
<xsl:value-of select="@url"/> |
||||
|
<xsl:text>>. </xsl:text> |
||||
|
<xsl:apply-templates select="automaton"> |
||||
|
<xsl:with-param name="filename" select="filename"/> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton"> |
||||
|
<xsl:param name="filename"/> |
||||
|
<xsl:text>digraph "</xsl:text> |
||||
|
<xsl:call-template name="escape"> |
||||
|
<xsl:with-param name="subject" select="$filename"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text>" { |
||||
|
node [fontname = courier, shape = box, colorscheme = paired6] |
||||
|
edge [fontname = courier] |
||||
|
|
||||
|
</xsl:text> |
||||
|
<xsl:apply-templates select="state"/> |
||||
|
<xsl:text>} </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton/state"> |
||||
|
<xsl:call-template name="output-node"> |
||||
|
<xsl:with-param name="number" select="@number"/> |
||||
|
<xsl:with-param name="label"> |
||||
|
<xsl:apply-templates select="itemset/item"/> |
||||
|
</xsl:with-param> |
||||
|
</xsl:call-template> |
||||
|
<xsl:apply-templates select="actions/transitions"/> |
||||
|
<xsl:apply-templates select="actions/reductions"> |
||||
|
<xsl:with-param name="staten"> |
||||
|
<xsl:value-of select="@number"/> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/reductions"> |
||||
|
<xsl:param name="staten"/> |
||||
|
<xsl:for-each select='reduction'> |
||||
|
<!-- These variables are needed because the current context can't be |
||||
|
referred to directly in XPath expressions. --> |
||||
|
<xsl:variable name="rul"> |
||||
|
<xsl:value-of select="@rule"/> |
||||
|
</xsl:variable> |
||||
|
<xsl:variable name="ena"> |
||||
|
<xsl:value-of select="@enabled"/> |
||||
|
</xsl:variable> |
||||
|
<!-- The foreach's body is protected by this, so that we are actually |
||||
|
going to iterate once per reduction rule, and not per lookahead. --> |
||||
|
<xsl:if test='not(preceding-sibling::*[@rule=$rul and @enabled=$ena])'> |
||||
|
<xsl:variable name="rule"> |
||||
|
<xsl:choose> |
||||
|
<!-- The acceptation state is referred to as 'accept' in the XML, but |
||||
|
just as '0' in the DOT. --> |
||||
|
<xsl:when test="@rule='accept'"> |
||||
|
<xsl:text>0</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="@rule"/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:variable> |
||||
|
|
||||
|
<!-- The edge's beginning --> |
||||
|
<xsl:call-template name="reduction-edge-start"> |
||||
|
<xsl:with-param name="state" select="$staten"/> |
||||
|
<xsl:with-param name="rule" select="$rule"/> |
||||
|
<xsl:with-param name="enabled" select="@enabled"/> |
||||
|
</xsl:call-template> |
||||
|
|
||||
|
<!-- The edge's tokens --> |
||||
|
<!-- Don't show labels for the default action. In other cases, there will |
||||
|
always be at least one token, so 'label="[]"' will not occur. --> |
||||
|
<xsl:if test='$rule!=0 and not(../reduction[@enabled=$ena and @rule=$rule and @symbol="$default"])'> |
||||
|
<xsl:text>label="[</xsl:text> |
||||
|
<xsl:for-each select='../reduction[@enabled=$ena and @rule=$rule]'> |
||||
|
<xsl:call-template name="escape"> |
||||
|
<xsl:with-param name="subject" select="@symbol"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:if test="position() != last ()"> |
||||
|
<xsl:text>, </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
<xsl:text>]", </xsl:text> |
||||
|
</xsl:if> |
||||
|
|
||||
|
<!-- The edge's end --> |
||||
|
<xsl:text>style=solid] </xsl:text> |
||||
|
|
||||
|
<!-- The diamond representing the reduction --> |
||||
|
<xsl:call-template name="reduction-node"> |
||||
|
<xsl:with-param name="state" select="$staten"/> |
||||
|
<xsl:with-param name="rule" select="$rule"/> |
||||
|
<xsl:with-param name="color"> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test='@enabled="true"'> |
||||
|
<xsl:text>3</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:text>5</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:with-param> |
||||
|
</xsl:call-template> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/transitions"> |
||||
|
<xsl:apply-templates select="transition"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="item"> |
||||
|
<xsl:param name="prev-rule-number" |
||||
|
select="preceding-sibling::item[1]/@rule-number"/> |
||||
|
<xsl:apply-templates select="key('bison:ruleByNumber', @rule-number)"> |
||||
|
<xsl:with-param name="dot" select="@dot"/> |
||||
|
<xsl:with-param name="num" select="@rule-number"/> |
||||
|
<xsl:with-param name="prev-lhs" |
||||
|
select="key('bison:ruleByNumber', $prev-rule-number)/lhs[text()]" |
||||
|
/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="lookaheads"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="rule"> |
||||
|
<xsl:param name="dot"/> |
||||
|
<xsl:param name="num"/> |
||||
|
<xsl:param name="prev-lhs"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$num < 10"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:when test="$num < 100"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:text></xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
<xsl:value-of select="$num"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$prev-lhs = lhs[text()]"> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="'|'"/> |
||||
|
<xsl:with-param name="pad" select="number(string-length(lhs[text()])) + 1"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="lhs"/> |
||||
|
<xsl:text>:</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
<xsl:if test="$dot = 0"> |
||||
|
<xsl:text> .</xsl:text> |
||||
|
</xsl:if> |
||||
|
|
||||
|
<!-- RHS --> |
||||
|
<xsl:for-each select="rhs/symbol|rhs/empty"> |
||||
|
<xsl:apply-templates select="."/> |
||||
|
<xsl:if test="$dot = position()"> |
||||
|
<xsl:text> .</xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="symbol"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="."/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="empty"> |
||||
|
<xsl:text> %empty</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="lookaheads"> |
||||
|
<xsl:text> [</xsl:text> |
||||
|
<xsl:apply-templates select="symbol"/> |
||||
|
<xsl:text>]</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="lookaheads/symbol"> |
||||
|
<xsl:value-of select="."/> |
||||
|
<xsl:if test="position() != last()"> |
||||
|
<xsl:text>, </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="reduction-edge-start"> |
||||
|
<xsl:param name="state"/> |
||||
|
<xsl:param name="rule"/> |
||||
|
<xsl:param name="enabled"/> |
||||
|
|
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="$state"/> |
||||
|
<xsl:text> -> "</xsl:text> |
||||
|
<xsl:value-of select="$state"/> |
||||
|
<xsl:text>R</xsl:text> |
||||
|
<xsl:value-of select="$rule"/> |
||||
|
<xsl:if test='$enabled = "false"'> |
||||
|
<xsl:text>d</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text>" [</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="reduction-node"> |
||||
|
<xsl:param name="state"/> |
||||
|
<xsl:param name="rule"/> |
||||
|
<xsl:param name="color"/> |
||||
|
|
||||
|
<xsl:text> "</xsl:text> |
||||
|
<xsl:value-of select="$state"/> |
||||
|
<xsl:text>R</xsl:text> |
||||
|
<xsl:value-of select="$rule"/> |
||||
|
<xsl:if test="$color = 5"> |
||||
|
<xsl:text>d</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text>" [label="</xsl:text> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$rule = 0"> |
||||
|
<xsl:text>Acc", fillcolor=1</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:text>R</xsl:text> |
||||
|
<xsl:value-of select="$rule"/> |
||||
|
<xsl:text>", fillcolor=</xsl:text> |
||||
|
<xsl:value-of select="$color"/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
<xsl:text>, shape=diamond, style=filled] </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="transition"> |
||||
|
<xsl:call-template name="output-edge"> |
||||
|
<xsl:with-param name="src" select="../../../@number"/> |
||||
|
<xsl:with-param name="dst" select="@state"/> |
||||
|
<xsl:with-param name="style"> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="@symbol = 'error'"> |
||||
|
<xsl:text>dotted</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:when test="@type = 'shift'"> |
||||
|
<xsl:text>solid</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:text>dashed</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:with-param> |
||||
|
<xsl:with-param name="label"> |
||||
|
<xsl:if test="not(@symbol = 'error')"> |
||||
|
<xsl:value-of select="@symbol"/> |
||||
|
</xsl:if> |
||||
|
</xsl:with-param> |
||||
|
</xsl:call-template> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="output-node"> |
||||
|
<xsl:param name="number"/> |
||||
|
<xsl:param name="label"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="$number"/> |
||||
|
<xsl:text> [label="</xsl:text> |
||||
|
<xsl:text>State </xsl:text> |
||||
|
<xsl:value-of select="$number"/> |
||||
|
<xsl:text>\n</xsl:text> |
||||
|
<xsl:call-template name="escape"> |
||||
|
<xsl:with-param name="subject" select="$label"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text>\l"] </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="output-edge"> |
||||
|
<xsl:param name="src"/> |
||||
|
<xsl:param name="dst"/> |
||||
|
<xsl:param name="style"/> |
||||
|
<xsl:param name="label"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="$src"/> |
||||
|
<xsl:text> -> </xsl:text> |
||||
|
<xsl:value-of select="$dst"/> |
||||
|
<xsl:text> [style=</xsl:text> |
||||
|
<xsl:value-of select="$style"/> |
||||
|
<xsl:if test="$label and $label != ''"> |
||||
|
<xsl:text> label="</xsl:text> |
||||
|
<xsl:call-template name="escape"> |
||||
|
<xsl:with-param name="subject" select="$label"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text>"</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text>] </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="escape"> |
||||
|
<xsl:param name="subject"/> <!-- required --> |
||||
|
<xsl:call-template name="string-replace"> |
||||
|
<xsl:with-param name="subject"> |
||||
|
<xsl:call-template name="string-replace"> |
||||
|
<xsl:with-param name="subject"> |
||||
|
<xsl:call-template name="string-replace"> |
||||
|
<xsl:with-param name="subject" select="$subject"/> |
||||
|
<xsl:with-param name="search" select="'\'"/> |
||||
|
<xsl:with-param name="replace" select="'\\'"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
<xsl:with-param name="search" select="'"'"/> |
||||
|
<xsl:with-param name="replace" select="'\"'"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
<xsl:with-param name="search" select="' '"/> |
||||
|
<xsl:with-param name="replace" select="'\l'"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="string-replace"> |
||||
|
<xsl:param name="subject"/> |
||||
|
<xsl:param name="search"/> |
||||
|
<xsl:param name="replace"/> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="contains($subject, $search)"> |
||||
|
<xsl:variable name="before" select="substring-before($subject, $search)"/> |
||||
|
<xsl:variable name="after" select="substring-after($subject, $search)"/> |
||||
|
<xsl:value-of select="$before"/> |
||||
|
<xsl:value-of select="$replace"/> |
||||
|
<xsl:call-template name="string-replace"> |
||||
|
<xsl:with-param name="subject" select="$after"/> |
||||
|
<xsl:with-param name="search" select="$search"/> |
||||
|
<xsl:with-param name="replace" select="$replace"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="$subject"/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="lpad"> |
||||
|
<xsl:param name="str" select="''"/> |
||||
|
<xsl:param name="pad" select="0"/> |
||||
|
<xsl:variable name="diff" select="$pad - string-length($str)" /> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$diff < 0"> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$diff"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
</xsl:stylesheet> |
||||
@ -0,0 +1,572 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<!-- |
||||
|
xml2text.xsl - transform Bison XML Report into plain text. |
||||
|
|
||||
|
Copyright (C) 2007-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
This file is part of Bison, the GNU Compiler Compiler. |
||||
|
|
||||
|
This program is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Written by Wojciech Polak <polak@gnu.org>. |
||||
|
--> |
||||
|
|
||||
|
<xsl:stylesheet version="1.0" |
||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
||||
|
xmlns:bison="https://www.gnu.org/software/bison/"> |
||||
|
|
||||
|
<xsl:import href="bison.xsl"/> |
||||
|
<xsl:output method="text" encoding="UTF-8" indent="no"/> |
||||
|
|
||||
|
<xsl:template match="/"> |
||||
|
<xsl:apply-templates select="bison-xml-report"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="bison-xml-report"> |
||||
|
<xsl:apply-templates select="grammar" mode="reductions"/> |
||||
|
<xsl:apply-templates select="grammar" mode="useless-in-parser"/> |
||||
|
<xsl:apply-templates select="automaton" mode="conflicts"/> |
||||
|
<xsl:apply-templates select="grammar"/> |
||||
|
<xsl:apply-templates select="automaton"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar" mode="reductions"> |
||||
|
<xsl:apply-templates select="nonterminals" mode="useless-in-grammar"/> |
||||
|
<xsl:apply-templates select="terminals" mode="unused-in-grammar"/> |
||||
|
<xsl:apply-templates select="rules" mode="useless-in-grammar"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="nonterminals" mode="useless-in-grammar"> |
||||
|
<xsl:if test="nonterminal[@usefulness='useless-in-grammar']"> |
||||
|
<xsl:text>Nonterminals useless in grammar </xsl:text> |
||||
|
<xsl:for-each select="nonterminal[@usefulness='useless-in-grammar']"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="@name"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:for-each> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="terminals" mode="unused-in-grammar"> |
||||
|
<xsl:if test="terminal[@usefulness='unused-in-grammar']"> |
||||
|
<xsl:text>Terminals unused in grammar </xsl:text> |
||||
|
<xsl:for-each select="terminal[@usefulness='unused-in-grammar']"> |
||||
|
<xsl:sort select="@symbol-number" data-type="number"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="@name"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:for-each> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="rules" mode="useless-in-grammar"> |
||||
|
<xsl:variable name="set" select="rule[@usefulness='useless-in-grammar']"/> |
||||
|
<xsl:if test="$set"> |
||||
|
<xsl:text>Rules useless in grammar </xsl:text> |
||||
|
<xsl:call-template name="style-rule-set"> |
||||
|
<xsl:with-param name="rule-set" select="$set"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar" mode="useless-in-parser"> |
||||
|
<xsl:variable |
||||
|
name="set" select="rules/rule[@usefulness='useless-in-parser']" |
||||
|
/> |
||||
|
<xsl:if test="$set"> |
||||
|
<xsl:text>Rules useless in parser due to conflicts </xsl:text> |
||||
|
<xsl:call-template name="style-rule-set"> |
||||
|
<xsl:with-param name="rule-set" select="$set"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar"> |
||||
|
<xsl:text>Grammar </xsl:text> |
||||
|
<xsl:call-template name="style-rule-set"> |
||||
|
<xsl:with-param |
||||
|
name="rule-set" select="rules/rule[@usefulness!='useless-in-grammar']" |
||||
|
/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="terminals"/> |
||||
|
<xsl:apply-templates select="nonterminals"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="style-rule-set"> |
||||
|
<xsl:param name="rule-set"/> |
||||
|
<xsl:for-each select="$rule-set"> |
||||
|
<xsl:apply-templates select="."> |
||||
|
<xsl:with-param name="pad" select="'3'"/> |
||||
|
<xsl:with-param name="prev-lhs"> |
||||
|
<xsl:if test="position()>1"> |
||||
|
<xsl:variable name="position" select="position()"/> |
||||
|
<xsl:value-of select="$rule-set[$position - 1]/lhs"/> |
||||
|
</xsl:if> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:for-each> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar/terminals"> |
||||
|
<xsl:text>Terminals, with rules where they appear </xsl:text> |
||||
|
<xsl:apply-templates select="terminal"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar/nonterminals"> |
||||
|
<xsl:text>Nonterminals, with rules where they appear </xsl:text> |
||||
|
<xsl:apply-templates select="nonterminal[@usefulness!='useless-in-grammar']"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="terminal"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="@name"/> |
||||
|
<xsl:call-template name="line-wrap"> |
||||
|
<xsl:with-param name="first-line-length"> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="string-length(@name) > 66">0</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="66 - string-length(@name)" /> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:with-param> |
||||
|
<xsl:with-param name="line-length" select="66" /> |
||||
|
<xsl:with-param name="text"> |
||||
|
<xsl:if test="string-length(@type) != 0"> |
||||
|
<xsl:value-of select="concat(' <', @type, '>')"/> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="concat(' (', @token-number, ')')"/> |
||||
|
<xsl:for-each select="key('bison:ruleByRhs', @name)"> |
||||
|
<xsl:value-of select="concat(' ', @number)"/> |
||||
|
</xsl:for-each> |
||||
|
</xsl:with-param> |
||||
|
</xsl:call-template> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="nonterminal"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="@name"/> |
||||
|
<xsl:if test="string-length(@type) != 0"> |
||||
|
<xsl:value-of select="concat(' <', @type, '>')"/> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="concat(' (', @symbol-number, ')')"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:variable name="output"> |
||||
|
<xsl:call-template name="line-wrap"> |
||||
|
<xsl:with-param name="line-length" select="66" /> |
||||
|
<xsl:with-param name="text"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:if test="key('bison:ruleByLhs', @name)"> |
||||
|
<xsl:text>on@left:</xsl:text> |
||||
|
<xsl:for-each select="key('bison:ruleByLhs', @name)"> |
||||
|
<xsl:value-of select="concat(' ', @number)"/> |
||||
|
</xsl:for-each> |
||||
|
</xsl:if> |
||||
|
<xsl:if test="key('bison:ruleByRhs', @name)"> |
||||
|
<xsl:if test="key('bison:ruleByLhs', @name)"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text>on@right:</xsl:text> |
||||
|
<xsl:for-each select="key('bison:ruleByRhs', @name)"> |
||||
|
<xsl:value-of select="concat(' ', @number)"/> |
||||
|
</xsl:for-each> |
||||
|
</xsl:if> |
||||
|
</xsl:with-param> |
||||
|
</xsl:call-template> |
||||
|
</xsl:variable> |
||||
|
<xsl:value-of select="translate($output, '@', ' ')" /> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton" mode="conflicts"> |
||||
|
<xsl:variable name="conflict-report"> |
||||
|
<xsl:apply-templates select="state" mode="conflicts"/> |
||||
|
</xsl:variable> |
||||
|
<xsl:if test="string-length($conflict-report) != 0"> |
||||
|
<xsl:value-of select="$conflict-report"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="state" mode="conflicts"> |
||||
|
<xsl:variable name="conflict-counts"> |
||||
|
<xsl:apply-templates select="." mode="bison:count-conflicts" /> |
||||
|
</xsl:variable> |
||||
|
<xsl:variable |
||||
|
name="sr-count" select="substring-before($conflict-counts, ',')" |
||||
|
/> |
||||
|
<xsl:variable |
||||
|
name="rr-count" select="substring-after($conflict-counts, ',')" |
||||
|
/> |
||||
|
<xsl:if test="$sr-count > 0 or $rr-count > 0"> |
||||
|
<xsl:value-of select="concat('State ', @number, ' conflicts:')"/> |
||||
|
<xsl:if test="$sr-count > 0"> |
||||
|
<xsl:value-of select="concat(' ', $sr-count, ' shift/reduce')"/> |
||||
|
<xsl:if test="$rr-count > 0"> |
||||
|
<xsl:value-of select="(',')"/> |
||||
|
</xsl:if> |
||||
|
</xsl:if> |
||||
|
<xsl:if test="$rr-count > 0"> |
||||
|
<xsl:value-of select="concat(' ', $rr-count, ' reduce/reduce')"/> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="' '"/> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton"> |
||||
|
<xsl:apply-templates select="state"> |
||||
|
<xsl:with-param name="pad" select="'3'"/> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton/state"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:text>State </xsl:text> |
||||
|
<xsl:value-of select="@number"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="itemset/item"> |
||||
|
<xsl:with-param name="pad" select="$pad"/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="actions/transitions"> |
||||
|
<xsl:with-param name="type" select="'shift'"/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="actions/errors"/> |
||||
|
<xsl:apply-templates select="actions/reductions"/> |
||||
|
<xsl:apply-templates select="actions/transitions"> |
||||
|
<xsl:with-param name="type" select="'goto'"/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="solved-conflicts"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/transitions"> |
||||
|
<xsl:param name="type"/> |
||||
|
<xsl:if test="transition[@type = $type]"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="transition[@type = $type]"> |
||||
|
<xsl:with-param name="pad"> |
||||
|
<xsl:call-template name="max-width-symbol"> |
||||
|
<xsl:with-param name="node" select="transition[@type = $type]"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/errors"> |
||||
|
<xsl:if test="error"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="error"> |
||||
|
<xsl:with-param name="pad"> |
||||
|
<xsl:call-template name="max-width-symbol"> |
||||
|
<xsl:with-param name="node" select="error"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/reductions"> |
||||
|
<xsl:if test="reduction"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="reduction"> |
||||
|
<xsl:with-param name="pad"> |
||||
|
<xsl:call-template name="max-width-symbol"> |
||||
|
<xsl:with-param name="node" select="reduction"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="item"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:param name="prev-rule-number" |
||||
|
select="preceding-sibling::item[1]/@rule-number"/> |
||||
|
<xsl:apply-templates |
||||
|
select="key('bison:ruleByNumber', current()/@rule-number)" |
||||
|
> |
||||
|
<xsl:with-param name="itemset" select="'true'"/> |
||||
|
<xsl:with-param name="pad" select="$pad"/> |
||||
|
<xsl:with-param |
||||
|
name="prev-lhs" |
||||
|
select="key('bison:ruleByNumber', $prev-rule-number)/lhs[text()]" |
||||
|
/> |
||||
|
<xsl:with-param name="dot" select="@dot"/> |
||||
|
<xsl:with-param name="lookaheads"> |
||||
|
<xsl:apply-templates select="lookaheads"/> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="rule"> |
||||
|
<xsl:param name="itemset"/> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:param name="prev-lhs"/> |
||||
|
<xsl:param name="dot"/> |
||||
|
<xsl:param name="lookaheads"/> |
||||
|
|
||||
|
<xsl:if test="$itemset != 'true' and not($prev-lhs = lhs[text()])"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
|
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="string(@number)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad)"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text> </xsl:text> |
||||
|
|
||||
|
<!-- LHS --> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$itemset != 'true' and $prev-lhs = lhs[text()]"> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="'|'"/> |
||||
|
<xsl:with-param name="pad" select="number(string-length(lhs[text()])) + 1"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:when> |
||||
|
<xsl:when test="$itemset = 'true' and $prev-lhs = lhs[text()]"> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="'|'"/> |
||||
|
<xsl:with-param name="pad" select="number(string-length(lhs[text()])) + 1"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="lhs"/> |
||||
|
<xsl:text>:</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
|
||||
|
<!-- RHS --> |
||||
|
<xsl:for-each select="rhs/*"> |
||||
|
<xsl:if test="position() = $dot + 1"> |
||||
|
<xsl:text> •</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:apply-templates select="."/> |
||||
|
<xsl:if test="position() = last() and position() = $dot"> |
||||
|
<xsl:text> •</xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
<xsl:if test="$lookaheads"> |
||||
|
<xsl:value-of select="$lookaheads"/> |
||||
|
</xsl:if> |
||||
|
|
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="symbol"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="."/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="empty"> |
||||
|
<xsl:text> %empty</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="lookaheads"> |
||||
|
<xsl:text> [</xsl:text> |
||||
|
<xsl:apply-templates select="symbol"/> |
||||
|
<xsl:text>]</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="lookaheads/symbol"> |
||||
|
<xsl:value-of select="."/> |
||||
|
<xsl:if test="position() != last()"> |
||||
|
<xsl:text>, </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="transition"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="rpad"> |
||||
|
<xsl:with-param name="str" select="string(@symbol)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="@type = 'shift'"> |
||||
|
<xsl:text>shift, and go to state </xsl:text> |
||||
|
<xsl:value-of select="@state"/> |
||||
|
</xsl:when> |
||||
|
<xsl:when test="@type = 'goto'"> |
||||
|
<xsl:text>go to state </xsl:text> |
||||
|
<xsl:value-of select="@state"/> |
||||
|
</xsl:when> |
||||
|
</xsl:choose> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="error"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="rpad"> |
||||
|
<xsl:with-param name="str" select="string(@symbol)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text>error</xsl:text> |
||||
|
<xsl:text> (</xsl:text> |
||||
|
<xsl:value-of select="text()"/> |
||||
|
<xsl:text>)</xsl:text> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="reduction"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="rpad"> |
||||
|
<xsl:with-param name="str" select="string(@symbol)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:if test="@enabled = 'false'"> |
||||
|
<xsl:text>[</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="@rule = 'accept'"> |
||||
|
<xsl:text>accept</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:text>reduce using rule </xsl:text> |
||||
|
<xsl:value-of select="@rule"/> |
||||
|
<xsl:text> (</xsl:text> |
||||
|
<xsl:value-of |
||||
|
select="key('bison:ruleByNumber', current()/@rule)/lhs[text()]"/> |
||||
|
<xsl:text>)</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
<xsl:if test="@enabled = 'false'"> |
||||
|
<xsl:text>]</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="solved-conflicts"> |
||||
|
<xsl:if test="resolution"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="resolution"/> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="resolution"> |
||||
|
<xsl:text> Conflict between rule </xsl:text> |
||||
|
<xsl:value-of select="@rule"/> |
||||
|
<xsl:text> and token </xsl:text> |
||||
|
<xsl:value-of select="@symbol"/> |
||||
|
<xsl:text> resolved as </xsl:text> |
||||
|
<xsl:if test="@type = 'error'"> |
||||
|
<xsl:text>an </xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="@type"/> |
||||
|
<xsl:text> (</xsl:text> |
||||
|
<xsl:value-of select="."/> |
||||
|
<xsl:text>). </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="max-width-symbol"> |
||||
|
<xsl:param name="node"/> |
||||
|
<xsl:variable name="longest"> |
||||
|
<xsl:for-each select="$node"> |
||||
|
<xsl:sort data-type="number" select="string-length(@symbol)" |
||||
|
order="descending"/> |
||||
|
<xsl:if test="position() = 1"> |
||||
|
<xsl:value-of select="string-length(@symbol)"/> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
</xsl:variable> |
||||
|
<xsl:value-of select="$longest"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="lpad"> |
||||
|
<xsl:param name="str" select="''"/> |
||||
|
<xsl:param name="pad" select="0"/> |
||||
|
<xsl:variable name="diff" select="$pad - string-length($str)" /> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$diff < 0"> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$diff"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="rpad"> |
||||
|
<xsl:param name="str" select="''"/> |
||||
|
<xsl:param name="pad" select="0"/> |
||||
|
<xsl:variable name="diff" select="$pad - string-length($str)"/> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$diff < 0"> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$diff"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="line-wrap"> |
||||
|
<xsl:param name="line-length"/> <!-- required --> |
||||
|
<xsl:param name="first-line-length" select="$line-length"/> |
||||
|
<xsl:param name="text"/> <!-- required --> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="normalize-space($text) = ''" /> |
||||
|
<xsl:when test="string-length($text) <= $first-line-length"> |
||||
|
<xsl:value-of select="concat($text, ' ')" /> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:variable name="break-pos"> |
||||
|
<xsl:call-template name="ws-search"> |
||||
|
<xsl:with-param name="text" select="$text" /> |
||||
|
<xsl:with-param name="start" select="$first-line-length+1" /> |
||||
|
</xsl:call-template> |
||||
|
</xsl:variable> |
||||
|
<xsl:value-of select="substring($text, 1, $break-pos - 1)" /> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="line-wrap"> |
||||
|
<xsl:with-param name="line-length" select="$line-length" /> |
||||
|
<xsl:with-param |
||||
|
name="text" select="concat(' ', substring($text, $break-pos+1))" |
||||
|
/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="ws-search"> |
||||
|
<xsl:param name="text"/> <!-- required --> |
||||
|
<xsl:param name="start"/> <!-- required --> |
||||
|
<xsl:variable name="search-text" select="substring($text, $start)" /> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="not(contains($search-text, ' '))"> |
||||
|
<xsl:value-of select="string-length($text)+1" /> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of |
||||
|
select="$start + string-length(substring-before($search-text, ' '))" |
||||
|
/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
</xsl:stylesheet> |
||||
@ -0,0 +1,765 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<!-- |
||||
|
xml2html.xsl - transform Bison XML Report into XHTML. |
||||
|
|
||||
|
Copyright (C) 2007-2015, 2018-2021 Free Software Foundation, Inc. |
||||
|
|
||||
|
This file is part of Bison, the GNU Compiler Compiler. |
||||
|
|
||||
|
This program is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
This program is distributed in the hope that it will be useful, |
||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
GNU General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Written by Wojciech Polak <polak@gnu.org>. |
||||
|
--> |
||||
|
|
||||
|
<xsl:stylesheet version="1.0" |
||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
||||
|
xmlns="http://www.w3.org/1999/xhtml" |
||||
|
xmlns:bison="https://www.gnu.org/software/bison/"> |
||||
|
|
||||
|
<xsl:import href="bison.xsl"/> |
||||
|
|
||||
|
<xsl:output method="xml" encoding="UTF-8" |
||||
|
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" |
||||
|
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" |
||||
|
indent="yes"/> |
||||
|
|
||||
|
<xsl:template match="/"> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> |
||||
|
<title> |
||||
|
<xsl:value-of select="bison-xml-report/filename"/> |
||||
|
<xsl:text> - GNU Bison XML Automaton Report</xsl:text> |
||||
|
</title> |
||||
|
<style type="text/css"><![CDATA[ |
||||
|
body { |
||||
|
font-family: "Nimbus Sans L", Arial, sans-serif; |
||||
|
font-size: 9pt; |
||||
|
} |
||||
|
a:link { |
||||
|
color: #1f00ff; |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
a:visited { |
||||
|
color: #1f00ff; |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
a:hover { |
||||
|
color: red; |
||||
|
} |
||||
|
#menu a { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
.i { |
||||
|
font-style: italic; |
||||
|
} |
||||
|
.pre { |
||||
|
font-family: monospace; |
||||
|
white-space: pre; |
||||
|
} |
||||
|
ol.decimal { |
||||
|
list-style-type: decimal; |
||||
|
} |
||||
|
ol.lower-alpha { |
||||
|
list-style-type: lower-alpha; |
||||
|
} |
||||
|
.dot { |
||||
|
color: #cc0000; |
||||
|
} |
||||
|
#footer { |
||||
|
margin-top: 3.5em; |
||||
|
font-size: 7pt; |
||||
|
} |
||||
|
]]></style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<xsl:apply-templates select="bison-xml-report"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<div id="footer"><hr />This document was generated using |
||||
|
<a href="https://www.gnu.org/software/bison/" title="GNU Bison"> |
||||
|
GNU Bison <xsl:value-of select="/bison-xml-report/@version"/></a> |
||||
|
XML Automaton Report.<br /> |
||||
|
<!-- default copying notice --> |
||||
|
Verbatim copying and distribution of this entire page is |
||||
|
permitted in any medium, provided this notice is preserved.</div> |
||||
|
</body> |
||||
|
</html> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="bison-xml-report"> |
||||
|
<h1>GNU Bison XML Automaton Report</h1> |
||||
|
<p> |
||||
|
input grammar: <span class="i"><xsl:value-of select="filename"/></span> |
||||
|
</p> |
||||
|
|
||||
|
<xsl:text> </xsl:text> |
||||
|
<h3>Table of Contents</h3> |
||||
|
<ul id="menu"> |
||||
|
<li> |
||||
|
<a href="#reductions">Reductions</a> |
||||
|
<ul class="lower-alpha"> |
||||
|
<li><a href="#nonterminals_useless_in_grammar">Nonterminals useless in grammar</a></li> |
||||
|
<li><a href="#terminals_unused_in_grammar">Terminals unused in grammar</a></li> |
||||
|
<li><a href="#rules_useless_in_grammar">Rules useless in grammar</a></li> |
||||
|
<xsl:if test="grammar/rules/rule[@usefulness='useless-in-parser']"> |
||||
|
<li><a href="#rules_useless_in_parser">Rules useless in parser due to conflicts</a></li> |
||||
|
</xsl:if> |
||||
|
</ul> |
||||
|
</li> |
||||
|
<li><a href="#conflicts">Conflicts</a></li> |
||||
|
<li> |
||||
|
<a href="#grammar">Grammar</a> |
||||
|
<ul class="lower-alpha"> |
||||
|
<li><a href="#grammar">Itemset</a></li> |
||||
|
<li><a href="#terminals">Terminal symbols</a></li> |
||||
|
<li><a href="#nonterminals">Nonterminal symbols</a></li> |
||||
|
</ul> |
||||
|
</li> |
||||
|
<li><a href="#automaton">Automaton</a></li> |
||||
|
</ul> |
||||
|
<xsl:apply-templates select="grammar" mode="reductions"/> |
||||
|
<xsl:apply-templates select="grammar" mode="useless-in-parser"/> |
||||
|
<xsl:apply-templates select="automaton" mode="conflicts"/> |
||||
|
<xsl:apply-templates select="grammar"/> |
||||
|
<xsl:apply-templates select="automaton"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar" mode="reductions"> |
||||
|
<h2> |
||||
|
<a name="reductions"/> |
||||
|
<xsl:text> Reductions</xsl:text> |
||||
|
</h2> |
||||
|
<xsl:apply-templates select="nonterminals" mode="useless-in-grammar"/> |
||||
|
<xsl:apply-templates select="terminals" mode="unused-in-grammar"/> |
||||
|
<xsl:apply-templates select="rules" mode="useless-in-grammar"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="nonterminals" mode="useless-in-grammar"> |
||||
|
<h3> |
||||
|
<a name="nonterminals_useless_in_grammar"/> |
||||
|
<xsl:text> Nonterminals useless in grammar</xsl:text> |
||||
|
</h3> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:if test="nonterminal[@usefulness='useless-in-grammar']"> |
||||
|
<p class="pre"> |
||||
|
<xsl:for-each select="nonterminal[@usefulness='useless-in-grammar']"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="@name"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:for-each> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</p> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="terminals" mode="unused-in-grammar"> |
||||
|
<h3> |
||||
|
<a name="terminals_unused_in_grammar"/> |
||||
|
<xsl:text> Terminals unused in grammar</xsl:text> |
||||
|
</h3> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:if test="terminal[@usefulness='unused-in-grammar']"> |
||||
|
<p class="pre"> |
||||
|
<xsl:for-each select="terminal[@usefulness='unused-in-grammar']"> |
||||
|
<xsl:sort select="@symbol-number" data-type="number"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:value-of select="@name"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:for-each> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</p> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="rules" mode="useless-in-grammar"> |
||||
|
<h3> |
||||
|
<a name="rules_useless_in_grammar"/> |
||||
|
<xsl:text> Rules useless in grammar</xsl:text> |
||||
|
</h3> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:variable name="set" select="rule[@usefulness='useless-in-grammar']"/> |
||||
|
<xsl:if test="$set"> |
||||
|
<p class="pre"> |
||||
|
<xsl:call-template name="style-rule-set"> |
||||
|
<xsl:with-param name="rule-set" select="$set"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</p> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar" mode="useless-in-parser"> |
||||
|
<xsl:variable |
||||
|
name="set" select="rules/rule[@usefulness='useless-in-parser']" |
||||
|
/> |
||||
|
<xsl:if test="$set"> |
||||
|
<h2> |
||||
|
<a name="rules_useless_in_parser"/> |
||||
|
<xsl:text> Rules useless in parser due to conflicts</xsl:text> |
||||
|
</h2> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<p class="pre"> |
||||
|
<xsl:call-template name="style-rule-set"> |
||||
|
<xsl:with-param name="rule-set" select="$set"/> |
||||
|
</xsl:call-template> |
||||
|
</p> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar"> |
||||
|
<h2> |
||||
|
<a name="grammar"/> |
||||
|
<xsl:text> Grammar</xsl:text> |
||||
|
</h2> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<p class="pre"> |
||||
|
<xsl:call-template name="style-rule-set"> |
||||
|
<xsl:with-param name="anchor" select="'true'" /> |
||||
|
<xsl:with-param |
||||
|
name="rule-set" select="rules/rule[@usefulness!='useless-in-grammar']" |
||||
|
/> |
||||
|
</xsl:call-template> |
||||
|
</p> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="terminals"/> |
||||
|
<xsl:apply-templates select="nonterminals"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="style-rule-set"> |
||||
|
<xsl:param name="anchor"/> |
||||
|
<xsl:param name="rule-set"/> |
||||
|
<xsl:for-each select="$rule-set"> |
||||
|
<xsl:apply-templates select="."> |
||||
|
<xsl:with-param name="anchor" select="$anchor"/> |
||||
|
<xsl:with-param name="pad" select="'3'"/> |
||||
|
<xsl:with-param name="prev-lhs"> |
||||
|
<xsl:if test="position()>1"> |
||||
|
<xsl:variable name="position" select="position()"/> |
||||
|
<xsl:value-of select="$rule-set[$position - 1]/lhs"/> |
||||
|
</xsl:if> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:for-each> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton" mode="conflicts"> |
||||
|
<h2> |
||||
|
<a name="conflicts"/> |
||||
|
<xsl:text> Conflicts</xsl:text> |
||||
|
</h2> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:variable name="conflict-report"> |
||||
|
<xsl:apply-templates select="state" mode="conflicts"/> |
||||
|
</xsl:variable> |
||||
|
<xsl:if test="string-length($conflict-report) != 0"> |
||||
|
<p class="pre"> |
||||
|
<xsl:copy-of select="$conflict-report"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</p> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="state" mode="conflicts"> |
||||
|
<xsl:variable name="conflict-counts"> |
||||
|
<xsl:apply-templates select="." mode="bison:count-conflicts" /> |
||||
|
</xsl:variable> |
||||
|
<xsl:variable |
||||
|
name="sr-count" select="substring-before($conflict-counts, ',')" |
||||
|
/> |
||||
|
<xsl:variable |
||||
|
name="rr-count" select="substring-after($conflict-counts, ',')" |
||||
|
/> |
||||
|
<xsl:if test="$sr-count > 0 or $rr-count > 0"> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#state_', @number)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:value-of select="concat('State ', @number)"/> |
||||
|
</a> |
||||
|
<xsl:text> conflicts:</xsl:text> |
||||
|
<xsl:if test="$sr-count > 0"> |
||||
|
<xsl:value-of select="concat(' ', $sr-count, ' shift/reduce')"/> |
||||
|
<xsl:if test="$rr-count > 0"> |
||||
|
<xsl:value-of select="(',')"/> |
||||
|
</xsl:if> |
||||
|
</xsl:if> |
||||
|
<xsl:if test="$rr-count > 0"> |
||||
|
<xsl:value-of select="concat(' ', $rr-count, ' reduce/reduce')"/> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="' '"/> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar/terminals"> |
||||
|
<h3> |
||||
|
<a name="terminals"/> |
||||
|
<xsl:text> Terminals, with rules where they appear</xsl:text> |
||||
|
</h3> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<ul> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="terminal"/> |
||||
|
</ul> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="grammar/nonterminals"> |
||||
|
<h3> |
||||
|
<a name="nonterminals"/> |
||||
|
<xsl:text> Nonterminals, with rules where they appear</xsl:text> |
||||
|
</h3> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<ul> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates |
||||
|
select="nonterminal[@usefulness!='useless-in-grammar']" |
||||
|
/> |
||||
|
</ul> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="terminal"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<li> |
||||
|
<b><xsl:value-of select="@name"/></b> |
||||
|
<xsl:if test="string-length(@type) != 0"> |
||||
|
<xsl:value-of select="concat(' <', @type, '>')"/> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="concat(' (', @token-number, ')')"/> |
||||
|
<xsl:for-each select="key('bison:ruleByRhs', @name)"> |
||||
|
<xsl:apply-templates select="." mode="number-link"/> |
||||
|
</xsl:for-each> |
||||
|
</li> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="nonterminal"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<li> |
||||
|
<b><xsl:value-of select="@name"/></b> |
||||
|
<xsl:if test="string-length(@type) != 0"> |
||||
|
<xsl:value-of select="concat(' <', @type, '>')"/> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="concat(' (', @symbol-number, ')')"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<ul> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:if test="key('bison:ruleByLhs', @name)"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<li> |
||||
|
<xsl:text>on left:</xsl:text> |
||||
|
<xsl:for-each select="key('bison:ruleByLhs', @name)"> |
||||
|
<xsl:apply-templates select="." mode="number-link"/> |
||||
|
</xsl:for-each> |
||||
|
</li> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:if test="key('bison:ruleByRhs', @name)"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<li> |
||||
|
<xsl:text>on right:</xsl:text> |
||||
|
<xsl:for-each select="key('bison:ruleByRhs', @name)"> |
||||
|
<xsl:apply-templates select="." mode="number-link"/> |
||||
|
</xsl:for-each> |
||||
|
</li> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</ul> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</li> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="rule" mode="number-link"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#rule_', @number)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:value-of select="@number"/> |
||||
|
</a> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton"> |
||||
|
<h2> |
||||
|
<a name="automaton"/> |
||||
|
<xsl:text> Automaton</xsl:text> |
||||
|
</h2> |
||||
|
<xsl:apply-templates select="state"> |
||||
|
<xsl:with-param name="pad" select="'3'"/> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="automaton/state"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<h3> |
||||
|
<a> |
||||
|
<xsl:attribute name="name"> |
||||
|
<xsl:value-of select="concat('state_', @number)"/> |
||||
|
</xsl:attribute> |
||||
|
</a> |
||||
|
<xsl:text>State </xsl:text> |
||||
|
<xsl:value-of select="@number"/> |
||||
|
</h3> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<p class="pre"> |
||||
|
<xsl:apply-templates select="itemset/item"> |
||||
|
<xsl:with-param name="pad" select="$pad"/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="actions/transitions"> |
||||
|
<xsl:with-param name="type" select="'shift'"/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="actions/errors"/> |
||||
|
<xsl:apply-templates select="actions/reductions"/> |
||||
|
<xsl:apply-templates select="actions/transitions"> |
||||
|
<xsl:with-param name="type" select="'goto'"/> |
||||
|
</xsl:apply-templates> |
||||
|
<xsl:apply-templates select="solved-conflicts"/> |
||||
|
</p> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/transitions"> |
||||
|
<xsl:param name="type"/> |
||||
|
<xsl:if test="transition[@type = $type]"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="transition[@type = $type]"> |
||||
|
<xsl:with-param name="pad"> |
||||
|
<xsl:call-template name="max-width-symbol"> |
||||
|
<xsl:with-param name="node" select="transition[@type = $type]"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/errors"> |
||||
|
<xsl:if test="error"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="error"> |
||||
|
<xsl:with-param name="pad"> |
||||
|
<xsl:call-template name="max-width-symbol"> |
||||
|
<xsl:with-param name="node" select="error"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="actions/reductions"> |
||||
|
<xsl:if test="reduction"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="reduction"> |
||||
|
<xsl:with-param name="pad"> |
||||
|
<xsl:call-template name="max-width-symbol"> |
||||
|
<xsl:with-param name="node" select="reduction"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="item"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:param name="prev-rule-number" |
||||
|
select="preceding-sibling::item[1]/@rule-number"/> |
||||
|
<xsl:apply-templates |
||||
|
select="key('bison:ruleByNumber', current()/@rule-number)" |
||||
|
> |
||||
|
<xsl:with-param name="itemset" select="'true'"/> |
||||
|
<xsl:with-param name="pad" select="$pad"/> |
||||
|
<xsl:with-param name="prev-lhs" |
||||
|
select="key('bison:ruleByNumber', $prev-rule-number)/lhs[text()]" |
||||
|
/> |
||||
|
<xsl:with-param name="dot" select="@dot"/> |
||||
|
<xsl:with-param name="lookaheads"> |
||||
|
<xsl:apply-templates select="lookaheads"/> |
||||
|
</xsl:with-param> |
||||
|
</xsl:apply-templates> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<!-- |
||||
|
anchor = 'true': define as an <a> anchor. |
||||
|
itemset = 'true': show the items. |
||||
|
--> |
||||
|
<xsl:template match="rule"> |
||||
|
<xsl:param name="anchor"/> |
||||
|
<xsl:param name="itemset"/> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:param name="prev-lhs"/> |
||||
|
<xsl:param name="dot"/> |
||||
|
<xsl:param name="lookaheads"/> |
||||
|
|
||||
|
<xsl:if test="$itemset != 'true' and not($prev-lhs = lhs[text()])"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:if> |
||||
|
|
||||
|
<xsl:text> </xsl:text> |
||||
|
|
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$anchor = 'true'"> |
||||
|
<a> |
||||
|
<xsl:attribute name="name"> |
||||
|
<xsl:value-of select="concat('rule_', @number)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="string(@number)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad)"/> |
||||
|
</xsl:call-template> |
||||
|
</a> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#rule_', @number)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="string(@number)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad)"/> |
||||
|
</xsl:call-template> |
||||
|
</a> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
<xsl:text> </xsl:text> |
||||
|
|
||||
|
<!-- LHS --> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$prev-lhs = lhs[text()]"> |
||||
|
<xsl:call-template name="lpad"> |
||||
|
<xsl:with-param name="str" select="'|'"/> |
||||
|
<xsl:with-param name="pad" select="number(string-length(lhs[text()])) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<span class="i"> |
||||
|
<xsl:value-of select="lhs"/> |
||||
|
</span> |
||||
|
<xsl:text> →</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
|
||||
|
<!-- RHS --> |
||||
|
<xsl:for-each select="rhs/*"> |
||||
|
<xsl:if test="position() = $dot + 1"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<span class="dot">•</span> |
||||
|
</xsl:if> |
||||
|
<xsl:apply-templates select="."/> |
||||
|
<xsl:if test="position() = last() and position() = $dot"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<span class="dot">•</span> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
<xsl:if test="$lookaheads"> |
||||
|
<xsl:value-of select="$lookaheads"/> |
||||
|
</xsl:if> |
||||
|
|
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="symbol"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="name(key('bison:symbolByName', .)) = 'nonterminal'"> |
||||
|
<span class="i"><xsl:value-of select="."/></span> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<b><xsl:value-of select="."/></b> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="empty"> |
||||
|
<xsl:text> %empty</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="lookaheads"> |
||||
|
<xsl:text> [</xsl:text> |
||||
|
<xsl:apply-templates select="symbol"/> |
||||
|
<xsl:text>]</xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="lookaheads/symbol"> |
||||
|
<xsl:value-of select="."/> |
||||
|
<xsl:if test="position() != last()"> |
||||
|
<xsl:text>, </xsl:text> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="transition"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="rpad"> |
||||
|
<xsl:with-param name="str" select="string(@symbol)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="@type = 'shift'"> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#state_', @state)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:value-of select="concat('shift, and go to state ', @state)"/> |
||||
|
</a> |
||||
|
</xsl:when> |
||||
|
<xsl:when test="@type = 'goto'"> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#state_', @state)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:value-of select="concat('go to state ', @state)"/> |
||||
|
</a> |
||||
|
</xsl:when> |
||||
|
</xsl:choose> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="error"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="rpad"> |
||||
|
<xsl:with-param name="str" select="string(@symbol)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:text>error</xsl:text> |
||||
|
<xsl:text> (</xsl:text> |
||||
|
<xsl:value-of select="text()"/> |
||||
|
<xsl:text>)</xsl:text> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="reduction"> |
||||
|
<xsl:param name="pad"/> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:call-template name="rpad"> |
||||
|
<xsl:with-param name="str" select="string(@symbol)"/> |
||||
|
<xsl:with-param name="pad" select="number($pad) + 2"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:if test="@enabled = 'false'"> |
||||
|
<xsl:text>[</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="@rule = 'accept'"> |
||||
|
<xsl:text>accept</xsl:text> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#rule_', @rule)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:value-of select="concat('reduce using rule ', @rule)"/> |
||||
|
</a> |
||||
|
<xsl:text> (</xsl:text> |
||||
|
<xsl:value-of |
||||
|
select="key('bison:ruleByNumber', current()/@rule)/lhs[text()]" |
||||
|
/> |
||||
|
<xsl:text>)</xsl:text> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
<xsl:if test="@enabled = 'false'"> |
||||
|
<xsl:text>]</xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:text> </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="solved-conflicts"> |
||||
|
<xsl:if test="resolution"> |
||||
|
<xsl:text> </xsl:text> |
||||
|
<xsl:apply-templates select="resolution"/> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template match="resolution"> |
||||
|
<xsl:text> Conflict between </xsl:text> |
||||
|
<a> |
||||
|
<xsl:attribute name="href"> |
||||
|
<xsl:value-of select="concat('#rule_', @rule)"/> |
||||
|
</xsl:attribute> |
||||
|
<xsl:value-of select="concat('rule ',@rule)"/> |
||||
|
</a> |
||||
|
<xsl:text> and token </xsl:text> |
||||
|
<xsl:value-of select="@symbol"/> |
||||
|
<xsl:text> resolved as </xsl:text> |
||||
|
<xsl:if test="@type = 'error'"> |
||||
|
<xsl:text>an </xsl:text> |
||||
|
</xsl:if> |
||||
|
<xsl:value-of select="@type"/> |
||||
|
<xsl:text> (</xsl:text> |
||||
|
<xsl:value-of select="."/> |
||||
|
<xsl:text>). </xsl:text> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="max-width-symbol"> |
||||
|
<xsl:param name="node"/> |
||||
|
<xsl:variable name="longest"> |
||||
|
<xsl:for-each select="$node"> |
||||
|
<xsl:sort data-type="number" select="string-length(@symbol)" |
||||
|
order="descending"/> |
||||
|
<xsl:if test="position() = 1"> |
||||
|
<xsl:value-of select="string-length(@symbol)"/> |
||||
|
</xsl:if> |
||||
|
</xsl:for-each> |
||||
|
</xsl:variable> |
||||
|
<xsl:value-of select="$longest"/> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="lpad"> |
||||
|
<xsl:param name="str" select="''"/> |
||||
|
<xsl:param name="pad" select="0"/> |
||||
|
<xsl:variable name="diff" select="$pad - string-length($str)" /> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$diff < 0"> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$diff"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="rpad"> |
||||
|
<xsl:param name="str" select="''"/> |
||||
|
<xsl:param name="pad" select="0"/> |
||||
|
<xsl:variable name="diff" select="$pad - string-length($str)"/> |
||||
|
<xsl:choose> |
||||
|
<xsl:when test="$diff < 0"> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
</xsl:when> |
||||
|
<xsl:otherwise> |
||||
|
<xsl:value-of select="$str"/> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$diff"/> |
||||
|
</xsl:call-template> |
||||
|
</xsl:otherwise> |
||||
|
</xsl:choose> |
||||
|
</xsl:template> |
||||
|
|
||||
|
<xsl:template name="space"> |
||||
|
<xsl:param name="repeat">0</xsl:param> |
||||
|
<xsl:param name="fill" select="' '"/> |
||||
|
<xsl:if test="number($repeat) >= 1"> |
||||
|
<xsl:call-template name="space"> |
||||
|
<xsl:with-param name="repeat" select="$repeat - 1"/> |
||||
|
<xsl:with-param name="fill" select="$fill"/> |
||||
|
</xsl:call-template> |
||||
|
<xsl:value-of select="$fill"/> |
||||
|
</xsl:if> |
||||
|
</xsl:template> |
||||
|
|
||||
|
</xsl:stylesheet> |
||||