Rileva la family table di assiemi e chiude i punti oscuri residui del parser .asm
AsmRecordReader.LeggiMembri ora lancia InvalidDataException quando un assieme e' esso stesso un'istanza di family table (members descriverebbe solo una rappresentazione, non le istanze sorelle) invece di restituire un risultato sbagliato in silenzio - il chiamante ripiega sull'euristica testuale, che risolve gia' correttamente il caso. Aggiunti due controlli di coerenza analoghi (conteggio grezzo vs interpretato dei record ConfigName; occorrenze multiple di "members"), verificati senza falsi positivi su tutti i 350 .asm reali della share. Documentazione riorganizzata per distinguere cio' che e' verificato contro una BOM Creo di riferimento da cio' che e' verificato solo su scala (conteggi), e cio' che resta genuinamente non risolto. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
28a72c80b2
commit
9af426ca1b
3 files changed
+215
-52
No files matched your search
@@ -49,12 +49,38 @@ public static class AsmRecordReader
|
||||
// normalizzazione ogni istanza del pattern risulterebbe un componente diverso.
|
||||
private static readonly Regex SuffissoArrayFeatureRegex = new(@"#AF\d+$", RegexOptions.Compiled);
|
||||
|
||||
// record di identita' del modello, seguito da "generic_name" (vuoto): compare su una
|
||||
// minoranza di assiemi (~7% dei campioni sulla share) per motivi diversi e nella
|
||||
// maggioranza dei casi "members" resta corretto - la sola presenza di "generic_name" NON
|
||||
// e' un segnale affidabile (verificato: su 144 file che lo contengono, solo 25 hanno
|
||||
// anche il sintomo sotto; per gli altri 119 "members" letto normalmente coincide con
|
||||
// l'euristica testuale, quindi e' un contesto diverso, non lo stesso problema).
|
||||
private static readonly Regex OwnModelIdentityRegex = new(
|
||||
@"\xe0\x01model_t_type\x00" + Int + @"\xe0\x0amodel_name\x00([\x20-\x7e]+)\x00\xe0\x0ageneric_name\x00\x00",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
// sintomo vero e proprio (docs/formato-asm-record.md §7): il file e' esso stesso
|
||||
// un'istanza di family table (il suo "model_name" ha il suffisso "_C0n" delle istanze,
|
||||
// stessa convenzione di SuffissoArrayFeatureRegex/ConfigName sopra) - "members" descrive
|
||||
// solo questa rappresentazione, non le istanze sorelle elencate in "stamp". Verificato su
|
||||
// 2546220101.asm: la BOM Creo reale mostra 4 istanze diverse, "members" ne restituisce
|
||||
// silenziosamente solo una.
|
||||
private static readonly Regex SuffissoIstanzaFamilyTableRegex = new(@"_C\d{2,}$", RegexOptions.Compiled);
|
||||
|
||||
// primo elemento dell'array "members": porta le chiavi per esteso.
|
||||
private static readonly Regex FirstMemberRegex = new(
|
||||
@"\x01id\x00" + Int + @"\xe0\x01type\x00" + Int
|
||||
+ @"\xe0\x0aname\x00([^\x00]+)\x00\xe0\x0afamily_name\x00([^\x00]+)\x00",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private static int ContaOccorrenze(string testo, string marcatore)
|
||||
{
|
||||
int conteggio = 0;
|
||||
for (int i = testo.IndexOf(marcatore, StringComparison.Ordinal); i >= 0; i = testo.IndexOf(marcatore, i + 1, StringComparison.Ordinal))
|
||||
conteggio++;
|
||||
return conteggio;
|
||||
}
|
||||
|
||||
private static int AsInt(string bytes)
|
||||
{
|
||||
int mask = bytes.Length switch { 1 => 0x7F, 2 => 0x3F, 3 => 0x1F, _ => throw new ArgumentException("lunghezza intero non valida") };
|
||||
@@ -84,14 +110,33 @@ public static class AsmRecordReader
|
||||
{
|
||||
string testo = Encoding.Latin1.GetString(dati);
|
||||
|
||||
var identita = OwnModelIdentityRegex.Match(testo);
|
||||
if (identita.Success && SuffissoIstanzaFamilyTableRegex.IsMatch(identita.Groups[2].Value))
|
||||
throw new InvalidDataException(
|
||||
$"assieme con family table: il file e' esso stesso l'istanza '{identita.Groups[2].Value}', " +
|
||||
"'members' descriverebbe solo questa rappresentazione, non le istanze sorelle - vedi " +
|
||||
"docs/formato-asm-record.md §7");
|
||||
|
||||
var configNameMatches = ConfigNameRegex.Matches(testo);
|
||||
int configNameGrezzi = ContaOccorrenze(testo, (char)0xe3 + "ConfigName\0");
|
||||
if (configNameMatches.Count != configNameGrezzi)
|
||||
throw new InvalidDataException(
|
||||
$"trovati {configNameGrezzi} record 'ConfigName' ma solo {configNameMatches.Count} " +
|
||||
"interpretati: formato del parametro diverso da quello atteso (vedi §4, costante " +
|
||||
"\\x82\\x7b), lettura non affidabile");
|
||||
|
||||
var istanze = new Dictionary<int, string>();
|
||||
foreach (Match m in ConfigNameRegex.Matches(testo))
|
||||
foreach (Match m in configNameMatches)
|
||||
istanze[AsInt(m.Groups[2].Value)] = SuffissoArrayFeatureRegex.Replace(m.Groups[1].Value, "");
|
||||
|
||||
const string chiaveMembers = "\0members\0";
|
||||
int start = testo.IndexOf(chiaveMembers, StringComparison.Ordinal);
|
||||
if (start < 0)
|
||||
throw new InvalidDataException("sezione 'members' assente");
|
||||
if (testo.IndexOf(chiaveMembers, start + chiaveMembers.Length, StringComparison.Ordinal) >= 0)
|
||||
throw new InvalidDataException(
|
||||
"trovate piu' occorrenze di 'members': probabile rappresentazione semplificata, " +
|
||||
"mai verificata (vedi §7) - non e' chiaro quale sezione descriva la BOM");
|
||||
start += chiaveMembers.Length;
|
||||
|
||||
int conteggio = dati[start + 2]; // \xf1 \xf8 <conteggio>
|
||||
|
||||
Reference in new issue
Block a user