Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ cargo test
## Key Design Decisions

- Follows hyperpolymath ABI-FFI standard (Idris2 ABI, Zig FFI)
- MPL-2.0 license
- MPL-2.0 license (code) + CC-BY-SA-4.0 (docs); full texts in LICENSES/
- RSR (Rhodium Standard Repository) template
- Author: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/rhodibot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Reads root-hygiene rules and auto-fixes what it can:
# - Delete banned files (AI.djot, duplicate CONTRIBUTING.adoc, stale snapshots)
# - Rename misnamed files (AI.a2ml → 0-AI-MANIFEST.a2ml)
# - Fix SPDX headers (AGPL → PMPL in dotfiles)
# - Fix SPDX headers (AGPL → MPL-2.0 in dotfiles)
# - Create missing required files (SECURITY.md, CONTRIBUTING.md)
# - Report unfixable issues as PR comments
#
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
for dotfile in .gitignore .gitattributes .editorconfig; do
if [ -f "$dotfile" ] && grep -q "AGPL-3.0" "$dotfile" 2>/dev/null; then
sed -i 's/AGPL-3.0-or-later/MPL-2.0/g; s/AGPL-3.0/MPL-2.0/g' "$dotfile"
FIXES="$FIXES\n- Fixed SPDX header in \`$dotfile\` (AGPL → PMPL)"
FIXES="$FIXES\n- Fixed SPDX header in \`$dotfile\` (AGPL → MPL-2.0)"
CHANGED=true
fi
done
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,8 @@ sync_report*.txt
# Hypatia scan cache (local-only)
.hypatia/
target/

# Idris2 build artefacts
**/build/
*.ttc
*.ttm
7 changes: 7 additions & 0 deletions .machine_readable/compliance/reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,10 @@ License: MPL-2.0
Files: cliff.toml
Copyright: {{CURRENT_YEAR}} {{AUTHOR}} ({{OWNER}}) <{{AUTHOR_EMAIL}}>
License: MPL-2.0

# Documentation prose is CC-BY-SA-4.0 (code/config is MPL-2.0).
# Last-match-wins in the Debian copyright format, so this overrides the
# `Files: *` default above for prose docs.
Files: *.adoc *.md docs/* docs/**/*
Copyright: {{CURRENT_YEAR}} {{AUTHOR}} ({{OWNER}}) <{{AUTHOR_EMAIL}}>
License: CC-BY-SA-4.0
2 changes: 1 addition & 1 deletion .machine_readable/contractiles/must/Mustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ These are hard requirements — CI fails if any check fails.
- severity: warning

### no-agpl
- description: No AGPL-3.0 references (superseded by PMPL)
- description: No AGPL-3.0 references (superseded by MPL-2.0)
- run: "! grep -r 'AGPL-3.0' .gitignore .gitattributes .editorconfig 2>/dev/null | head -1 | grep -q ."
- severity: critical

Expand Down
4 changes: 2 additions & 2 deletions .machine_readable/contractiles/trust/Trustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ is traceable.
- severity: warning

### license-content
- description: LICENSE contains PMPL identifier
- run: grep -q 'PMPL' LICENSE
- description: LICENSE contains MPL-2.0 identifier
- run: grep -q 'MPL-2.0' LICENSE
- severity: warning

### ci-pipeline-present
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version = "0.1.0"
edition = "2024"
authors = ["Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>"]
description = "Generate high-performance C libraries from high-level descriptions using Nim metaprogramming"
license-file = "LICENSE"
license = "MPL-2.0"
repository = "https://github.com/hyperpolymath/nimiser"
keywords = ["nim", "metaprogramming", "code-generation", "c-libraries"]
categories = ["command-line-utilities", "development-tools"]
Expand Down
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
SPDX-License-Identifier: MPL-2.0

Mozilla Public License Version 2.0
==================================

Expand Down
2 changes: 1 addition & 1 deletion QUICKSTART-MAINTAINER.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Or via OPSM: `opsm update {{PACKAGE_NAME}}`

== Security Notes

* License: MPL-2.0 (Palimpsest License)
* License: MPL-2.0 (code) / CC-BY-SA-4.0 (docs)
* All dependencies SHA-pinned
* `panic-attacker` scan results: link:INSTALL-SECURITY-REPORT.adoc[]
* OpenSSF Scorecard: see badge in README
Expand Down
2 changes: 1 addition & 1 deletion contractiles/trust/Trustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Maximal trust by default — LLM may read, build, test, lint, format.

### license-content
- description: LICENSE contains expected SPDX identifier
- run: grep -q 'SPDX\|License\|MIT\|Apache\|PMPL\|MPL' LICENSE
- run: grep -q 'SPDX\|MPL-2.0' LICENSE
- severity: critical

### no-secrets-committed
Expand Down
4 changes: 2 additions & 2 deletions docs/RSR_OUTLINE.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= RSR Template Repository

image:[Palimpsest-MPL-1.0,link="https://github.com/hyperpolymath/palimpsest-license"] image:[Palimpsest,link="https://github.com/hyperpolymath/palimpsest-license"]
image:https://img.shields.io/badge/license-MPL--2.0-blue[MPL-2.0,link="LICENSES/MPL-2.0.txt"] image:https://img.shields.io/badge/docs-CC--BY--SA--4.0-blue[CC-BY-SA-4.0,link="LICENSES/CC-BY-SA-4.0.txt"]
:toc:
:sectnums:

Expand Down Expand Up @@ -78,7 +78,7 @@ just validate-rsr
|Container build (Wolfi base, Podman)

|`LICENSE`
|MPL-2.0 (Palimpsest MPL)
|MPL-2.0 (code) / CC-BY-SA-4.0 (docs)

|`EXHIBIT-A-ETHICAL-USE.txt`
|Ethical use guidelines (LICENSE Exhibit A)
Expand Down
2 changes: 1 addition & 1 deletion docs/STATE-VISUALIZER.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ CONTAINER ECOSYSTEM (Phase 2)
REPO INFRASTRUCTURE
.machine_readable/ ██████████ 100% STATE/META/ECOSYSTEM active
Governance & License ██████████ 100% PMPL & Ethical use verified
Governance & License ██████████ 100% MPL-2.0 & Ethical use verified
Development Shells (Nix/Guix) ██████████ 100% Reproducible env stable
─────────────────────────────────────────────────────────────────────────────
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ export
free : Handle -> IO ()
free h = primIO (prim__free (handlePtr h))

--------------------------------------------------------------------------------
-- String Marshalling (declared early; used by the generation wrappers below)
--------------------------------------------------------------------------------

||| Convert C string to Idris String
export
%foreign "support:idris2_getString, libidris2_support"
prim__getString : Bits64 -> String

--------------------------------------------------------------------------------
-- Nim Compilation Pipeline
--------------------------------------------------------------------------------
Expand Down Expand Up @@ -102,7 +111,9 @@ genTemplate h tmpl = do
(if tmpl.exported then 1 else 0))
if ptr == 0
then pure (Left TemplateError)
else pure (Right (prim__getString ptr))
else do
let str = prim__getString ptr
pure (Right str)

||| Generate Nim macro code from a macro definition
||| Returns a C string containing generated Nim source, or null on error.
Expand All @@ -121,17 +132,14 @@ genMacro h mac = do
(if mac.generatesExport then 1 else 0))
if ptr == 0
then pure (Left MacroError)
else pure (Right (prim__getString ptr))
else do
let str = prim__getString ptr
pure (Right str)

--------------------------------------------------------------------------------
-- String Operations
--------------------------------------------------------------------------------

||| Convert C string to Idris String
export
%foreign "support:idris2_getString, libidris2_support"
prim__getString : Bits64 -> String

||| Free C string allocated by the Nim library
export
%foreign "C:nimiser_free_string, libnimiser"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ module Nimiser.ABI.Layout
import Nimiser.ABI.Types
import Data.Vect
import Data.So
import Data.Nat
import Decidable.Equality

%default total

Expand All @@ -33,24 +35,38 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
paddingFor offset alignment =
if offset `mod` alignment == 0
then 0
else alignment - (offset `mod` alignment)
else minus alignment (offset `mod` alignment)

||| Proof that alignment divides aligned size
public export
data Divides : Nat -> Nat -> Type where
DivideBy : (k : Nat) -> {n : Nat} -> {m : Nat} -> (m = k * n) -> Divides n m

||| Sound decision procedure: does n divide m?
||| For n = S j, compute q = div m (S j) and check m == q * (S j).
||| Division does not reduce definitionally, so the equality is checked, not
||| asserted.
public export
decDivides : (n : Nat) -> (m : Nat) -> Maybe (Divides n m)
decDivides Z _ = Nothing
decDivides (S j) m =
let q = div m (S j) in
case decEq m (q * (S j)) of
Yes prf => Just (DivideBy q prf)
No _ => Nothing

||| Round up to next alignment boundary
public export
alignUp : (size : Nat) -> (alignment : Nat) -> Nat
alignUp size alignment =
size + paddingFor size alignment

||| Proof that alignUp produces aligned result
||| Decide whether alignUp produced an aligned result for a given alignment.
||| (Division does not reduce definitionally, so this is decided rather than
||| proven by `Refl`; a universally-quantified `Refl` proof would be unsound.)
public export
alignUpCorrect : (size : Nat) -> (align : Nat) -> (align > 0) -> Divides align (alignUp size align)
alignUpCorrect size align prf =
DivideBy ((size + paddingFor size align) `div` align) Refl
alignUpCorrect : (size : Nat) -> (align : Nat) -> Maybe (Divides align (alignUp size align))
alignUpCorrect size align = decDivides align (alignUp size align)

--------------------------------------------------------------------------------
-- Struct Field Layout
Expand Down Expand Up @@ -82,7 +98,7 @@ record StructLayout where

||| Calculate total struct size with padding
public export
calcStructSize : Vect n Field -> Nat -> Nat
calcStructSize : Vect k Field -> Nat -> Nat
calcStructSize [] align = 0
calcStructSize (f :: fs) align =
let lastOffset = foldl (\acc, field => nextFieldOffset field) f.offset fs
Expand All @@ -91,23 +107,28 @@ calcStructSize (f :: fs) align =

||| Proof that field offsets are correctly aligned
public export
data FieldsAligned : Vect n Field -> Type where
data FieldsAligned : Vect k Field -> Type where
NoFields : FieldsAligned []
ConsField :
(f : Field) ->
(rest : Vect n Field) ->
(rest : Vect k Field) ->
Divides f.alignment f.offset ->
FieldsAligned rest ->
FieldsAligned (f :: rest)

||| Verify a struct layout is valid
public export
verifyLayout : (fields : Vect n Field) -> (align : Nat) -> Either String StructLayout
verifyLayout : (fields : Vect k Field) -> (align : Nat) -> Either String StructLayout
verifyLayout fields align =
let size = calcStructSize fields align
in case decSo (size >= sum (map (\f => f.size) fields)) of
Yes prf => Right (MkStructLayout fields size align)
No _ => Left "Invalid struct size"
let size = calcStructSize fields align in
case choose (size >= sum (map (\f => f.size) fields)) of
Right _ => Left "Invalid struct size"
Left sizeOk =>
case decDivides align size of
Nothing => Left "Total size is not a multiple of the alignment"
Just alignProof =>
Right (MkStructLayout fields size align
{sizeCorrect = sizeOk} {aligned = alignProof})

--------------------------------------------------------------------------------
-- Nim Object Layout Rules
Expand Down Expand Up @@ -148,7 +169,7 @@ nimFieldAlign p ty = min (nimFieldSize p ty) (ptrSize p `div` 8)
||| Convert a NimObject to a StructLayout using exportc rules
||| (C-compatible: declaration order, natural alignment, trailing padding)
public export
nimObjectToLayout : Platform -> NimObject -> Either String StructLayout
nimObjectToLayout : Platform -> NimObjectDef -> Either String StructLayout
nimObjectToLayout p obj =
let fields = computeFields p 0 obj.fields
in verifyLayout (fromList fields) (maxAlign p obj.fields)
Expand Down Expand Up @@ -200,10 +221,25 @@ data CABICompliant : StructLayout -> Type where
FieldsAligned layout.fields ->
CABICompliant layout

||| Decide whether every field's offset is aligned to its alignment.
public export
decFieldsAligned : (fields : Vect k Field) -> Maybe (FieldsAligned fields)
decFieldsAligned [] = Just NoFields
decFieldsAligned (f :: fs) =
case decDivides f.alignment f.offset of
Nothing => Nothing
Just dv =>
case decFieldsAligned fs of
Nothing => Nothing
Just rest => Just (ConsField f fs dv rest)

||| Check if layout follows C ABI
public export
checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
checkCABI layout = Right (CABIOk layout ?fieldsAlignedProof)
checkCABI layout =
case decFieldsAligned layout.fields of
Just prf => Right (CABIOk layout prf)
Nothing => Left "Struct fields are not correctly aligned for C ABI"

--------------------------------------------------------------------------------
-- Nim-Specific Layout Examples
Expand All @@ -224,6 +260,8 @@ nimBufferLayout =
]
16 -- Total size: 16 bytes
8 -- Alignment: 8 bytes (pointer)
{sizeCorrect = Oh}
{aligned = DivideBy 2 Refl}

||| Example: Nim object with default alignment
||| type NimResult {.exportc: "nimiser_result".} = object
Expand All @@ -239,16 +277,25 @@ nimResultLayout =
]
16 -- Total size: 16 bytes
8 -- Alignment: 8 bytes
{sizeCorrect = Oh}
{aligned = DivideBy 2 Refl}

||| Proof that buffer layout is valid
export
nimBufferLayoutValid : CABICompliant nimBufferLayout
nimBufferLayoutValid = CABIOk nimBufferLayout ?bufferFieldsAligned
nimBufferLayoutValid : CABICompliant Layout.nimBufferLayout
nimBufferLayoutValid =
CABIOk nimBufferLayout
(ConsField _ _ (DivideBy 0 Refl)
(ConsField _ _ (DivideBy 2 Refl)
(ConsField _ _ (DivideBy 3 Refl) NoFields)))

||| Proof that result layout is valid
export
nimResultLayoutValid : CABICompliant nimResultLayout
nimResultLayoutValid = CABIOk nimResultLayout ?resultFieldsAligned
nimResultLayoutValid : CABICompliant Layout.nimResultLayout
nimResultLayoutValid =
CABIOk nimResultLayout
(ConsField _ _ (DivideBy 0 Refl)
(ConsField _ _ (DivideBy 1 Refl) NoFields))

--------------------------------------------------------------------------------
-- Offset Calculation
Expand All @@ -262,7 +309,11 @@ fieldOffset layout name =
Just idx => Just (finToNat idx ** index idx layout.fields)
Nothing => Nothing

||| Proof that field offset is within struct bounds
||| Decide whether a field's offset is within struct bounds.
||| In general a field may exceed the struct, so the result is a Maybe witness.
public export
offsetInBounds : (layout : StructLayout) -> (f : Field) -> So (f.offset + f.size <= layout.totalSize)
offsetInBounds layout f = ?offsetInBoundsProof
offsetInBounds : (layout : StructLayout) -> (f : Field) -> Maybe (So (f.offset + f.size <= layout.totalSize))
offsetInBounds layout f =
case choose (f.offset + f.size <= layout.totalSize) of
Left ok => Just ok
Right _ => Nothing
Loading
Loading