-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDimensionsMetadata.cs
More file actions
191 lines (167 loc) · 5.83 KB
/
DimensionsMetadata.cs
File metadata and controls
191 lines (167 loc) · 5.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright (c) ktsu.dev
// All rights reserved.
// Licensed under the MIT license.
namespace Semantics.SourceGenerators.Models;
using System.Collections.Generic;
/// <summary>
/// Root metadata structure for physical dimensions.
/// </summary>
public class DimensionsMetadata
{
public List<PhysicalDimension> PhysicalDimensions { get; set; } = [];
/// <summary>
/// Validates the deserialised metadata, returning a list of human-readable issues.
/// An empty list means the metadata is well-formed.
/// </summary>
/// <remarks>
/// Per #60: catches malformed entries before they reach the generator emit
/// pass, so problems surface as Roslyn diagnostics rather than a mid-emit crash
/// or a silently dropped operator. Cross-dimension reference checks are still
/// performed in the generator (#56 / SEM001) because they need the full
/// dimension map.
/// </remarks>
public List<string> Validate()
{
List<string> issues = [];
if (PhysicalDimensions.Count == 0)
{
issues.Add("dimensions.json contains no physicalDimensions entries.");
return issues;
}
HashSet<string> seenDimensionNames = [];
HashSet<string> seenTypeNames = [];
foreach (PhysicalDimension dim in PhysicalDimensions)
{
string label = string.IsNullOrEmpty(dim.Name) ? "<unnamed>" : dim.Name;
if (string.IsNullOrEmpty(dim.Name))
{
issues.Add("A physicalDimensions entry is missing 'name'.");
}
else if (!seenDimensionNames.Add(dim.Name))
{
issues.Add($"Dimension '{dim.Name}' is declared more than once.");
}
if (string.IsNullOrEmpty(dim.Symbol))
{
issues.Add($"Dimension '{label}' is missing 'symbol'.");
}
if (dim.AvailableUnits.Count == 0)
{
issues.Add($"Dimension '{label}' has an empty 'availableUnits' list.");
}
else
{
foreach (string unit in dim.AvailableUnits)
{
if (string.IsNullOrWhiteSpace(unit))
{
issues.Add($"Dimension '{label}' has a blank entry in 'availableUnits'.");
}
}
}
VectorFormDefinition?[] forms = [
dim.Quantities.Vector0,
dim.Quantities.Vector1,
dim.Quantities.Vector2,
dim.Quantities.Vector3,
dim.Quantities.Vector4,
];
if (System.Array.TrueForAll(forms, f => f == null))
{
issues.Add($"Dimension '{label}' declares no vector forms (vector0..vector4).");
}
for (int i = 0; i < forms.Length; i++)
{
VectorFormDefinition? form = forms[i];
if (form == null)
{
continue;
}
if (string.IsNullOrEmpty(form.Base))
{
issues.Add($"Dimension '{label}' vector{i} is missing 'base'.");
}
else if (!seenTypeNames.Add(form.Base))
{
issues.Add($"Type name '{form.Base}' (dimension '{label}' vector{i}) collides with another base or overload.");
}
foreach (OverloadDefinition overload in form.Overloads)
{
if (string.IsNullOrEmpty(overload.Name))
{
issues.Add($"Dimension '{label}' vector{i} has an overload missing 'name'.");
continue;
}
if (!seenTypeNames.Add(overload.Name))
{
issues.Add($"Overload type name '{overload.Name}' (dimension '{label}' vector{i}) collides with another base or overload.");
}
}
}
}
return issues;
}
}
/// <summary>
/// Definition of a physical dimension with vector form quantities and relationships.
/// Relationships reference dimension names and are resolved to concrete types at each vector form.
/// </summary>
public class PhysicalDimension
{
public string Name { get; set; } = string.Empty;
public string Symbol { get; set; } = string.Empty;
public Dictionary<string, int> DimensionalFormula { get; set; } = [];
public List<string> AvailableUnits { get; set; } = [];
public VectorFormsMap Quantities { get; set; } = new();
public List<RelationshipDefinition> Integrals { get; set; } = [];
public List<RelationshipDefinition> Derivatives { get; set; } = [];
public List<RelationshipDefinition> DotProducts { get; set; } = [];
public List<RelationshipDefinition> CrossProducts { get; set; } = [];
}
/// <summary>
/// Map of vector forms for a physical dimension.
/// Each entry is nullable; absent entries mean that vector form is not available.
/// </summary>
public class VectorFormsMap
{
public VectorFormDefinition? Vector0 { get; set; }
public VectorFormDefinition? Vector1 { get; set; }
public VectorFormDefinition? Vector2 { get; set; }
public VectorFormDefinition? Vector3 { get; set; }
public VectorFormDefinition? Vector4 { get; set; }
}
/// <summary>
/// Definition of a single vector form within a dimension.
/// </summary>
public class VectorFormDefinition
{
/// <summary>Gets or sets the base type name for this vector form.</summary>
public string Base { get; set; } = string.Empty;
/// <summary>Gets or sets optional semantic overloads for this vector form.</summary>
public List<OverloadDefinition> Overloads { get; set; } = [];
}
/// <summary>
/// Definition of a semantic overload (typed alias) for a base quantity type.
/// </summary>
public class OverloadDefinition
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public Dictionary<string, string> Relationships { get; set; } = [];
}
/// <summary>
/// Definition of a mathematical relationship between dimensions.
/// </summary>
public class RelationshipDefinition
{
public string Other { get; set; } = string.Empty;
public string Result { get; set; } = string.Empty;
/// <summary>
/// Optional explicit list of vector forms (0..4) at which this relationship should
/// emit operators. When empty, the generator uses sensible defaults from the
/// relationship kind: <c>integrals</c>/<c>derivatives</c> default to all common forms,
/// <c>dotProducts</c> to V1+, <c>crossProducts</c> to V3 only. When set, missing forms
/// on either side surface as <c>SEM003</c> diagnostics instead of being silently dropped.
/// </summary>
public List<int> Forms { get; set; } = [];
}