nmhit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ ---
2
+ Language: Cpp
3
+ BasedOnStyle: LLVM
4
+
5
+ # Indentation
6
+ IndentWidth: 2
7
+ TabWidth: 2
8
+ UseTab: Never
9
+ ContinuationIndentWidth: 2
10
+ IndentCaseLabels: true
11
+
12
+ # Line length
13
+ ColumnLimit: 100
14
+
15
+ # Braces (Allman style — opening brace on its own line)
16
+ BreakBeforeBraces: Custom
17
+ BraceWrapping:
18
+ AfterCaseLabel: false
19
+ AfterClass: true
20
+ AfterControlStatement: Always
21
+ AfterEnum: true
22
+ AfterFunction: true
23
+ AfterNamespace: true
24
+ AfterStruct: true
25
+ AfterUnion: true
26
+ BeforeCatch: true
27
+ BeforeElse: true
28
+ IndentBraces: false
29
+ SplitEmptyFunction: false
30
+ SplitEmptyRecord: false
31
+ SplitEmptyNamespace: false
32
+
33
+ # Pointer / reference — middle spacing (int * p, not int* p or int *p)
34
+ PointerAlignment: Middle
35
+ ReferenceAlignment: Middle
36
+
37
+ # Spaces
38
+ SpaceAfterCStyleCast: false
39
+ SpaceBeforeAssignmentOperators: true
40
+ SpaceBeforeParens: ControlStatements
41
+ SpaceInEmptyParentheses: false
42
+ SpacesInAngles: Never
43
+ SpacesInContainerLiterals: false
44
+ SpacesInParentheses: false
45
+
46
+ # Return type on its own line for top-level definitions
47
+ AlwaysBreakAfterReturnType: TopLevelDefinitions
48
+
49
+ # Template declarations break to their own line
50
+ AlwaysBreakTemplateDeclarations: Yes
51
+
52
+ # Constructor initialiser lists
53
+ BreakConstructorInitializers: BeforeColon
54
+ ConstructorInitializerIndentWidth: 2
55
+
56
+ # Short constructs
57
+ AllowShortBlocksOnASingleLine: Empty
58
+ AllowShortCaseLabelsOnASingleLine: false
59
+ AllowShortFunctionsOnASingleLine: Inline
60
+ AllowShortIfStatementsOnASingleLine: Never
61
+ AllowShortLambdasOnASingleLine: Inline
62
+ AllowShortLoopsOnASingleLine: false
63
+
64
+ # Argument / parameter packing
65
+ AlignAfterOpenBracket: Align
66
+ BinPackArguments: false
67
+ BinPackParameters: false
68
+
69
+ # Alignment
70
+ AlignConsecutiveAssignments: false
71
+ AlignConsecutiveDeclarations: false
72
+ AlignOperands: Align
73
+ AlignTrailingComments: true
74
+
75
+ # Includes — preserve user ordering
76
+ SortIncludes: Never
77
+ IncludeBlocks: Preserve
78
+
79
+ # Blank lines
80
+ MaxEmptyLinesToKeep: 1
81
+ KeepEmptyLinesAtTheStartOfBlocks: false
@@ -0,0 +1,98 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ # ── Python binding build + test (editable install, fast feedback) ────────────
11
+ python:
12
+ name: "Python ${{ matrix.python-version }} / ${{ matrix.os }}"
13
+ runs-on: ${{ matrix.os }}
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ os: [ubuntu-24.04, macos-15]
18
+ python-version: ["3.9", "3.11", "3.12"]
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Install build and test dependencies
28
+ run: pip install scikit-build-core nanobind pytest
29
+
30
+ - name: Build and install (editable, no isolation)
31
+ run: pip install -e . --no-build-isolation -v
32
+
33
+ - name: Run Python tests
34
+ run: pytest python/tests/ -v
35
+
36
+ # ── Wheel + sdist packaging check (native arch only, no cross-compilation) ───
37
+ package:
38
+ name: "Package / ${{ matrix.os }}"
39
+ runs-on: ${{ matrix.os }}
40
+ strategy:
41
+ fail-fast: false
42
+ matrix:
43
+ os: [ubuntu-24.04, macos-15]
44
+
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+
48
+ - name: Build wheel (native arch)
49
+ uses: pypa/cibuildwheel@v2.19
50
+ env:
51
+ CIBW_BUILD: "cp39-*"
52
+ CIBW_SKIP: "*-musllinux* *-manylinux_i686"
53
+ CIBW_ARCHS_LINUX: "x86_64"
54
+ CIBW_ARCHS_MACOS: "native"
55
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
56
+ CIBW_TEST_REQUIRES: "pytest"
57
+ CIBW_TEST_COMMAND: "pytest {project}/python/tests/ -v"
58
+
59
+ # ── Source distribution build check ─────────────────────────────────────────
60
+ sdist:
61
+ name: "Source distribution"
62
+ runs-on: ubuntu-24.04
63
+
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+
67
+ - uses: actions/setup-python@v5
68
+ with:
69
+ python-version: "3.11"
70
+
71
+ - name: Build sdist
72
+ run: |
73
+ pip install build
74
+ python -m build --sdist
75
+
76
+ # ── C++ library tests (unmodified by Python changes) ────────────────────────
77
+ cpp:
78
+ name: "C++ / ${{ matrix.os }}"
79
+ runs-on: ${{ matrix.os }}
80
+ strategy:
81
+ fail-fast: false
82
+ matrix:
83
+ os: [ubuntu-24.04, macos-15]
84
+
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+
88
+ - name: Configure
89
+ run: >
90
+ cmake -S . -B build
91
+ -DCMAKE_BUILD_TYPE=Release
92
+ -DNMHIT_BUILD_PYTHON=OFF
93
+
94
+ - name: Build
95
+ run: cmake --build build -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
96
+
97
+ - name: Test
98
+ run: ctest --test-dir build --output-on-failure
@@ -0,0 +1,87 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ jobs:
9
+ # ── Build wheels (Linux + macOS) ─────────────────────────────────────────────
10
+ build-wheels:
11
+ name: "Wheels / ${{ matrix.os }}"
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-24.04, macos-15]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up QEMU (Linux arm64 cross-build)
22
+ if: matrix.os == 'ubuntu-24.04'
23
+ uses: docker/setup-qemu-action@v3
24
+ with:
25
+ platforms: arm64
26
+
27
+ - name: Build wheels
28
+ uses: pypa/cibuildwheel@v2.19
29
+ env:
30
+ # STABLE_ABI: cp39-* produces one abi3 wheel covering Python 3.9+
31
+ CIBW_BUILD: "cp39-*"
32
+ CIBW_SKIP: "*-musllinux* *-manylinux_i686"
33
+ CIBW_ARCHS_LINUX: "x86_64 aarch64"
34
+ CIBW_ARCHS_MACOS: "x86_64 arm64"
35
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
36
+ CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
37
+ CIBW_TEST_REQUIRES: "pytest"
38
+ CIBW_TEST_COMMAND: "pytest {project}/python/tests/ -v"
39
+
40
+ - uses: actions/upload-artifact@v4
41
+ with:
42
+ name: wheels-${{ matrix.os }}
43
+ path: wheelhouse/*.whl
44
+
45
+ # ── Build source distribution ─────────────────────────────────────────────────
46
+ build-sdist:
47
+ name: "Source distribution"
48
+ runs-on: ubuntu-24.04
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+
52
+ - uses: actions/setup-python@v5
53
+ with:
54
+ python-version: "3.11"
55
+
56
+ - name: Build sdist
57
+ run: |
58
+ pip install build
59
+ python -m build --sdist
60
+
61
+ - uses: actions/upload-artifact@v4
62
+ with:
63
+ name: sdist
64
+ path: dist/*.tar.gz
65
+
66
+ # ── Publish to PyPI via OIDC trusted publishing ───────────────────────────────
67
+ publish:
68
+ name: "Publish to PyPI"
69
+ needs: [build-wheels, build-sdist]
70
+ runs-on: ubuntu-24.04
71
+ environment:
72
+ name: pypi
73
+ url: https://pypi.org/p/nmhit
74
+ permissions:
75
+ id-token: write # required for OIDC trusted publishing
76
+
77
+ steps:
78
+ - uses: actions/download-artifact@v4
79
+ with:
80
+ path: dist/
81
+ merge-multiple: true
82
+
83
+ - name: Publish
84
+ uses: pypa/gh-action-pypi-publish@release/v1
85
+ # No API token needed — configure Trusted Publishing once on pypi.org:
86
+ # Publisher: GitHub Actions, repo: <owner>/nmhit, workflow: release.yml,
87
+ # environment: pypi
nmhit-0.1.0/.gitignore ADDED
@@ -0,0 +1,38 @@
1
+ # Cache directories
2
+ .cache/
3
+
4
+ # Build directories
5
+ build/
6
+ build-*/
7
+
8
+ # CMake generated files
9
+ CMakeCache.txt
10
+ CMakeFiles/
11
+ cmake_install.cmake
12
+ install_manifest.txt
13
+ CTestTestfile.cmake
14
+
15
+ # Compiled objects and libraries
16
+ *.o
17
+ *.a
18
+ *.so
19
+ *.so.*
20
+ *.dylib
21
+
22
+ # IDE and editor files
23
+ .vscode/
24
+ .idea/
25
+ *.swp
26
+ *.swo
27
+ *~
28
+ .DS_Store
29
+
30
+ # Python packaging
31
+ dist/
32
+ *.egg-info/
33
+ __pycache__/
34
+ *.pyc
35
+ *.pyi
36
+
37
+ # scikit-build-core build directory
38
+ _skbuild/
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/mirrors-clang-format
3
+ rev: v19.1.7
4
+ hooks:
5
+ - id: clang-format
6
+ # Only format hand-written C++ — skip generated/ and Flex/Bison sources.
7
+ files: ^(src/|include/|tests/|python/src/).+\.(cpp|h)$
@@ -0,0 +1,181 @@
1
+ cmake_minimum_required(VERSION 3.20)
2
+ project(neml2-hit VERSION 0.1.0 LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_CXX_EXTENSIONS OFF)
7
+
8
+ # ── Flex + Bison ───────────────────────────────────────────────────────────────
9
+ # Debug builds regenerate the parser/lexer from source (flex + bison required).
10
+ # All other build types (Release, RelWithDebInfo, MinSizeRel, …) use the
11
+ # pre-generated files committed to generated/ and need no flex or bison.
12
+
13
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR NOT CMAKE_BUILD_TYPE)
14
+ find_package(FLEX 2.6 REQUIRED)
15
+ find_package(BISON 3.7 REQUIRED)
16
+
17
+ FLEX_TARGET(
18
+ HITLexer
19
+ src/Lexer.l
20
+ ${CMAKE_CURRENT_BINARY_DIR}/Lexer.cpp
21
+ DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/Lexer.h
22
+ )
23
+
24
+ BISON_TARGET(
25
+ HITParser
26
+ src/Parser.y
27
+ ${CMAKE_CURRENT_BINARY_DIR}/Parser.cpp
28
+ DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/Parser.h
29
+ )
30
+
31
+ ADD_FLEX_BISON_DEPENDENCY(HITLexer HITParser)
32
+
33
+ set(_parser_sources
34
+ ${FLEX_HITLexer_OUTPUTS}
35
+ ${BISON_HITParser_OUTPUTS}
36
+ )
37
+ set(_parser_include_dir ${CMAKE_CURRENT_BINARY_DIR})
38
+
39
+ # Helper target: copy freshly-generated files back to generated/ for committing.
40
+ add_custom_target(update_generated
41
+ COMMAND ${CMAKE_COMMAND} -E copy
42
+ ${CMAKE_CURRENT_BINARY_DIR}/Lexer.cpp
43
+ ${CMAKE_CURRENT_BINARY_DIR}/Lexer.h
44
+ ${CMAKE_CURRENT_BINARY_DIR}/Parser.cpp
45
+ ${CMAKE_CURRENT_BINARY_DIR}/Parser.h
46
+ ${CMAKE_CURRENT_BINARY_DIR}/location.hh
47
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated/
48
+ DEPENDS
49
+ ${FLEX_HITLexer_OUTPUTS}
50
+ ${BISON_HITParser_OUTPUTS}
51
+ COMMENT "Copying generated parser/lexer files to generated/"
52
+ )
53
+ else()
54
+ set(_parser_sources
55
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated/Lexer.cpp
56
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated/Lexer.h
57
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated/Parser.cpp
58
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated/Parser.h
59
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated/location.hh
60
+ )
61
+ set(_parser_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/generated)
62
+ endif()
63
+
64
+ # ── library ───────────────────────────────────────────────────────────────────
65
+
66
+ add_library(nmhit
67
+ ${_parser_sources}
68
+ src/Node.cpp
69
+ src/BraceExpr.cpp
70
+ )
71
+
72
+ target_include_directories(nmhit
73
+ PUBLIC
74
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
75
+ $<INSTALL_INTERFACE:include>
76
+ PRIVATE
77
+ src
78
+ ${_parser_include_dir}
79
+ )
80
+
81
+ target_compile_options(nmhit PRIVATE -Wall -Wextra -Wno-unused-parameter)
82
+
83
+ # ── install ───────────────────────────────────────────────────────────────────
84
+ # C++ library install rules are skipped when building the Python wheel so that
85
+ # headers, CMake config files, and pkg-config files don't end up inside the wheel.
86
+
87
+ if(NOT NMHIT_BUILD_PYTHON)
88
+ include(CMakePackageConfigHelpers)
89
+
90
+ install(TARGETS nmhit
91
+ EXPORT nmhitTargets
92
+ ARCHIVE DESTINATION lib
93
+ LIBRARY DESTINATION lib
94
+ RUNTIME DESTINATION bin
95
+ )
96
+
97
+ install(DIRECTORY include/
98
+ DESTINATION include
99
+ )
100
+
101
+ configure_package_config_file(
102
+ cmake/nmhitConfig.cmake.in
103
+ ${CMAKE_CURRENT_BINARY_DIR}/nmhitConfig.cmake
104
+ INSTALL_DESTINATION lib/cmake/nmhit
105
+ )
106
+
107
+ write_basic_package_version_file(
108
+ ${CMAKE_CURRENT_BINARY_DIR}/nmhitConfigVersion.cmake
109
+ VERSION ${PROJECT_VERSION}
110
+ COMPATIBILITY SameMajorVersion
111
+ )
112
+
113
+ install(EXPORT nmhitTargets
114
+ FILE nmhitTargets.cmake
115
+ NAMESPACE nmhit::
116
+ DESTINATION lib/cmake/nmhit
117
+ )
118
+
119
+ install(FILES
120
+ ${CMAKE_CURRENT_BINARY_DIR}/nmhitConfig.cmake
121
+ ${CMAKE_CURRENT_BINARY_DIR}/nmhitConfigVersion.cmake
122
+ DESTINATION lib/cmake/nmhit
123
+ )
124
+
125
+ configure_file(
126
+ cmake/nmhit.pc.in
127
+ ${CMAKE_CURRENT_BINARY_DIR}/nmhit.pc
128
+ @ONLY
129
+ )
130
+
131
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nmhit.pc
132
+ DESTINATION lib/pkgconfig
133
+ )
134
+ endif()
135
+
136
+ # ── tests ──────────────────────────────────────────────────────────────────────
137
+
138
+ option(NMHIT_BUILD_TESTS "Build nmhit unit tests" ON)
139
+ if(NMHIT_BUILD_TESTS)
140
+ enable_testing()
141
+ add_subdirectory(tests)
142
+ endif()
143
+
144
+ # ── Python bindings (nanobind) ─────────────────────────────────────────────────
145
+ # Enabled by scikit-build-core via -DNMHIT_BUILD_PYTHON=ON.
146
+ # Release builds use the pre-generated parser/lexer in generated/ so flex and
147
+ # bison are not required on the wheel-build host.
148
+
149
+ option(NMHIT_BUILD_PYTHON "Build Python bindings via nanobind" OFF)
150
+ if(NMHIT_BUILD_PYTHON)
151
+ find_package(Python 3.9 REQUIRED COMPONENTS Interpreter Development.Module)
152
+
153
+ # Locate nanobind's CMake integration through the Python interpreter.
154
+ # scikit-build-core installs nanobind into the build venv so it is findable
155
+ # via `python -m nanobind --cmake_dir`.
156
+ execute_process(
157
+ COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
158
+ OUTPUT_STRIP_TRAILING_WHITESPACE
159
+ OUTPUT_VARIABLE _NB_CMAKE_DIR
160
+ RESULT_VARIABLE _NB_RESULT
161
+ )
162
+ if(NOT _NB_RESULT EQUAL 0)
163
+ message(FATAL_ERROR "Could not locate nanobind CMake directory. "
164
+ "Install it with: pip install nanobind")
165
+ endif()
166
+ list(APPEND CMAKE_PREFIX_PATH "${_NB_CMAKE_DIR}")
167
+ find_package(nanobind CONFIG REQUIRED)
168
+
169
+ nanobind_add_module(
170
+ _nmhit
171
+ STABLE_ABI # Py_LIMITED_API=0x03090000: one .so covers Python 3.9+
172
+ NB_STATIC # embed nanobind runtime; wheel is self-contained
173
+ python/src/_nmhit.cpp
174
+ )
175
+
176
+ target_link_libraries(_nmhit PRIVATE nmhit)
177
+
178
+ # Install into the nmhit/ subdirectory inside the wheel.
179
+ # scikit-build-core picks this up via wheel.packages = ["python/nmhit"].
180
+ install(TARGETS _nmhit DESTINATION nmhit)
181
+ endif()
@@ -0,0 +1,112 @@
1
+ # Contributing to neml2-hit
2
+
3
+ ## Setting up your development environment
4
+
5
+ ### 1. Clone and configure a Debug build
6
+
7
+ Flex ≥ 2.6 and Bison ≥ 3.7 are required for Debug builds; they regenerate
8
+ `generated/Lexer.cpp` and `generated/Parser.cpp` from source.
9
+
10
+ ```bash
11
+ git clone <repo-url>
12
+ cd neml2-hit
13
+ cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
14
+ cmake --build build -j$(nproc)
15
+ ```
16
+
17
+ ### 2. Set up the Python bindings (optional)
18
+
19
+ If you are working on `python/src/_nmhit.cpp` or `python/nmhit/__init__.py`,
20
+ install the package in editable mode after the CMake build:
21
+
22
+ ```bash
23
+ pip install scikit-build-core nanobind pytest # build deps + test runner
24
+ pip install -e . --no-build-isolation -v # editable install
25
+ pytest python/tests/ -v # run Python tests
26
+ ```
27
+
28
+ `--no-build-isolation` skips the PEP 517 build venv and uses the already-installed
29
+ `scikit-build-core` and `nanobind` packages directly, which avoids re-downloading them.
30
+ scikit-build-core places its own CMake build in `_skbuild/` (separate from any `build/`
31
+ you may have created for C++ development).
32
+ Re-run `pip install -e . --no-build-isolation -v` whenever you change the C++ binding source.
33
+
34
+ ### 3. Install the pre-commit hook
35
+
36
+ The project uses [pre-commit](https://pre-commit.com) to run `clang-format`
37
+ automatically before every commit. Install it once after cloning:
38
+
39
+ ```bash
40
+ pip install pre-commit # or: conda install pre-commit
41
+ pre-commit install
42
+ ```
43
+
44
+ From that point on, `clang-format` runs automatically on staged `*.cpp` and
45
+ `*.h` files under `src/`, `include/`, and `tests/`. Generated files in
46
+ `generated/` and the Flex/Bison sources (`src/Lexer.l`, `src/Parser.y`) are
47
+ intentionally excluded — no formatter exists for those file types.
48
+
49
+ To run the hook manually across the whole tree:
50
+
51
+ ```bash
52
+ pre-commit run --all-files
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Development workflow
58
+
59
+ ### Editing C++ source only
60
+
61
+ Make changes to `src/Node.cpp`, `src/BraceExpr.cpp`, `src/ParseDriver.h`,
62
+ the public headers under `include/`, or the Python binding source at
63
+ `python/src/_nmhit.cpp`, then rebuild:
64
+
65
+ ```bash
66
+ cmake --build build -j$(nproc)
67
+ ./build/tests/test_hit
68
+ ```
69
+
70
+ ### Editing the lexer or parser
71
+
72
+ `src/Lexer.l` (Flex) and `src/Parser.y` (Bison) require a Debug build. After
73
+ editing either file, the build system regenerates the corresponding sources
74
+ automatically:
75
+
76
+ ```bash
77
+ cmake --build build -j$(nproc) # flex/bison run here
78
+ ./build/tests/test_hit
79
+ ```
80
+
81
+ Once the tests pass, copy the regenerated files back to `generated/` so that
82
+ non-Debug (Release) builds remain self-contained without requiring flex or bison:
83
+
84
+ ```bash
85
+ cmake --build build --target update_generated
86
+ git add generated/
87
+ ```
88
+
89
+ Commit the grammar/lexer source and the regenerated files together in the same
90
+ commit so the repository is always consistent.
91
+
92
+ ---
93
+
94
+ ## Running the tests
95
+
96
+ ```bash
97
+ ./build/tests/test_hit # direct binary
98
+ # or via CTest:
99
+ ctest --test-dir build --output-on-failure
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Code style
105
+
106
+ `clang-format` (version 19) enforces the style defined in `.clang-format`.
107
+ The pre-commit hook applies it automatically; you can also run it by hand:
108
+
109
+ ```bash
110
+ clang-format -i src/Node.cpp src/BraceExpr.cpp src/ParseDriver.h \
111
+ include/nmhit/*.h tests/test_hit.cpp python/src/_nmhit.cpp
112
+ ```