diff --git a/CLAUDE.md b/CLAUDE.md index 8cce61c..2ba12f8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -472,6 +472,7 @@ var converted = sourceString.As(); - Rebuild `Semantics.SourceGenerators` and the consuming `Semantics.Quantities` project; emitted files appear in `Semantics.Quantities/Generated/Semantics.SourceGenerators//`. - Treat generator output as committed source. Diff it before commit so accidental regressions are visible. - After adding or renaming a quantity, regenerate the storage-type alias props with `pwsh scripts/Generate-AliasProps.ps1` (it reads the generated catalogue and rewrites `Semantics.Quantities.{Double,Float,Decimal,Precise}/build/*.props`) and commit them. The `verify-generated` workflow rebuilds, regenerates, and fails the PR if either the generated sources or the alias props drift. +- **Adding a storage type does not mean adding an entry to `precision.json`.** A new storage type is a new alias package alongside `Semantics.Quantities.{Double,Float,Decimal,Precise}`, its generated `build/*.props`, and one derived `StorageConversionTests` class — nothing else. `precision.json` looks like the place to declare it and is not, for two reasons (#236). `PrecisionGenerator` emits its `StorageTypes` class into the **core** `ktsu.Semantics.Quantities` namespace as `typeof({entry})`, so an entry whose type comes from a package would force the core assembly to reference that package — undoing the separation the alias packages exist to keep, which is exactly why `PreciseNumber` was left out. And the field name is the entry put through `ToUpperInvariant()`, so only a C# keyword yields an identifier: `ktsu.PreciseNumber.PreciseNumber` becomes `KTSU.PRECISENUMBER.PRECISENUMBER`, which does not compile. `StorageTypes` has no reader anywhere in the repository, so the omission costs nothing; it is public surface that enumerates three of the four shipped storage types, and #236 holds the open decision about whether it should exist at all. - Factory names are the **singular lemma** (#49). The generator emits `From{name}` using each unit's `name` from `units.json` verbatim (e.g. `Length.FromMeter`, `Mass.FromKilogram`, `Speed.FromMeterPerSecond`, `Length.FromFoot`, `Frequency.FromHertz`). The rule is purely mechanical, so `name` must itself be the singular lemma — including compounds, whose leading noun is singular too (`MeterPerSecond`, `RevolutionPerMinute`, `PartPerMillion`, not `MeterPerSecond`/`RevolutionPerMinute`/`PartPerMillion`). There is no `factoryName` field and no pluralisation step; the generator never has to know English pluralisation. - Generator diagnostics: - **SEM001** — a relationship in `dimensions.json` references a dimension that does not exist (typo or rename). The operator is silently dropped. diff --git a/docs/physics-generator.md b/docs/physics-generator.md index 2e5dcf6..6a1188c 100644 --- a/docs/physics-generator.md +++ b/docs/physics-generator.md @@ -12,7 +12,7 @@ For the *why* (the unified vector model), see `docs/strategy-unified-vector-quan | `UnitsGenerator` | `Units.g.cs` | All declared units with their conversion factors, as `double` properties and as `IUnit.ToBaseFactorAs()`/`ToBaseOffsetAs()` explicit implementations that read the per-type values. | | `ConversionsGenerator` | `ConversionConstants.g.cs` | Conversion ratios (`FeetToMeters`, etc.) from `conversions.json`, as `double` constants plus a `Values` holder that parses each one into the storage type. See [Conversion factor values](#conversion-factor-values). | | `MagnitudesGenerator` | `MetricMagnitudes.g.cs` | SI prefixes and their numeric magnitudes, as public `double` constants plus an internal `Values` holder parsed per storage type. | -| `PrecisionGenerator` | `StorageTypes.g.cs` | The storage types the alias packages cover (`decimal`, `double`, `float`), as a public `StorageTypes` class. Nothing in the library reads it. | +| `PrecisionGenerator` | `StorageTypes.g.cs` | The entries of `precision.json` (`decimal`, `double`, `float`), as a public `StorageTypes` class. Nothing in the library reads it, and it is **not** the list of supported storage types — that is the set of alias packages, which includes `Precise`. Do not add a package-provided type here; see [Storage types and `precision.json`](#storage-types-and-precisionjson). | | `PhysicalConstantsGenerator` | `PhysicalConstants.g.cs` | `PhysicalConstants..X()`, `PhysicalConstants.Generic.X()`, and `PhysicalConstants.Conversion.X()` accessors; each literal is parsed straight into `T` and cached per closed generic type. | | `QuantitiesGenerator` | one `*.g.cs` file per emitted type | Vector0/V1/V2/V3/V4 bases, semantic overloads, factories, operators, magnitude extraction, dot/cross products. | | `LogarithmicScalesGenerator` | one `*.g.cs` file per logarithmic scale | Decibel levels, pitch intervals, and pH from `logarithmic.json`: standalone `readonly partial record struct`s with linear-quantity conversions, log-space arithmetic, and comparisons. | @@ -219,6 +219,38 @@ than returning an estimate. so an application computing a norm the generator does not emit reaches them rather than reimplementing them. See the type's own documentation for what each one guarantees. +## Storage types and `precision.json` + +Four storage types ship, one per alias package: `Semantics.Quantities.Double`, `.Float`, `.Decimal` +and `.Precise`. `precision.json` lists three of them. That is deliberate, not an oversight, and the +missing entry is `PreciseNumber`. + +`PrecisionGenerator` emits `StorageTypes` into the **core** `ktsu.Semantics.Quantities` namespace, +one field per entry: + +```csharp +Name = storageType.ToUpperInvariant(), +DefaultValue = $"typeof({storageType})", +``` + +Two things follow, and both bite only on a type that is not a C# keyword: + +- `typeof(PreciseNumber)` in the core assembly would make `Semantics.Quantities` reference + `ktsu.PreciseNumber`. The alias packages exist precisely so that dependency stays opt-in, so the + entry would undo the separation it is meant to describe. +- The field name is derived from the entry, so a qualified name is not an identifier: + `ktsu.PreciseNumber.PreciseNumber` yields `KTSU.PRECISENUMBER.PRECISENUMBER`, which does not + compile. + +So **adding a storage type does not mean adding an entry here.** It means a new alias package, its +generated `build/*.props`, and one derived `StorageConversionTests` class. + +Nothing in the repository reads `StorageTypes` — outside `Generated/`, the only mentions are +`PrecisionGenerator` and `PrecisionMetadata` themselves — so the omission costs nothing at runtime. +It is still public surface that enumerates three of four shipped storage types, and whether it should +exist at all is the open question in +[#236](https://github.com/ktsu-dev/Semantics/issues/236). + ## Validation, diagnostics, and gotchas - Unknown dimension references in `integrals` / `derivatives` / `dotProducts` / `crossProducts` report **SEM001** and the operator is dropped.