mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2026-03-11 05:41:49 +00:00
Flatten symbols namespace
This commit is contained in:
@@ -1,22 +1,16 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
// file id=0,name="sudoku.s",size=9141,mtime=0x6027C7F0,mod=0
|
||||
[Section("file", "Files")]
|
||||
public sealed class File(Parser container) : NamedSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
// file id=0,name="sudoku.s",size=9141,mtime=0x6027C7F0,mod=0
|
||||
[Section("file", "Files")]
|
||||
public sealed class File(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionProperty("size")]
|
||||
public int Size { get; private set; }
|
||||
[SectionProperty("size")]
|
||||
public int Size { get; private set; }
|
||||
|
||||
[SectionProperty("mtime")]
|
||||
public DateTime ModificationTime { get; private set; }
|
||||
[SectionProperty("mtime")]
|
||||
public DateTime ModificationTime { get; private set; }
|
||||
|
||||
[SectionReference("mod")]
|
||||
public Symbols.Module? Module { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionReference("mod")]
|
||||
public Symbols.Module? Module { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,153 +1,147 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
public class IdentifiableSection : Section
|
||||
{
|
||||
namespace Symbols
|
||||
[SectionProperty("id")]
|
||||
public int ID { get; protected set; }
|
||||
|
||||
protected IdentifiableSection(Parser container)
|
||||
: base(container)
|
||||
{ }
|
||||
|
||||
private bool MaybeExtractFromParsed(string key, out string value)
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
public class IdentifiableSection : Section
|
||||
Debug.Assert(this._parsed != null);
|
||||
var found = this._parsed.TryGetValue(key, out var extracted);
|
||||
if (found)
|
||||
{
|
||||
[SectionProperty("id")]
|
||||
public int ID { get; protected set; }
|
||||
Debug.Assert(extracted != null);
|
||||
value = extracted;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = string.Empty;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
protected IdentifiableSection(Parser container)
|
||||
: base(container)
|
||||
{}
|
||||
private bool MaybeExtractIntegerFromParsed(string key, out int value)
|
||||
{
|
||||
var available = this.MaybeExtractFromParsed(key, out var extracted);
|
||||
if (!available)
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
value = ExtractInteger(extracted);
|
||||
return available;
|
||||
}
|
||||
|
||||
private bool MaybeExtractFromParsed(string key, out string value)
|
||||
protected bool MaybeExtractCompoundInteger(string key, out List<int> value)
|
||||
{
|
||||
var available = this.MaybeExtractFromParsed(key, out var extracted);
|
||||
if (!available)
|
||||
{
|
||||
value = [];
|
||||
return false;
|
||||
}
|
||||
value = ExtractCompoundInteger(extracted);
|
||||
return available;
|
||||
}
|
||||
|
||||
private void SetProperty(string name, object obj)
|
||||
{
|
||||
var type = this.GetType();
|
||||
var property = type.GetProperty(name);
|
||||
Debug.Assert(property != null);
|
||||
property.SetValue(this, obj);
|
||||
}
|
||||
|
||||
public void ExtractReferences()
|
||||
{
|
||||
this.ExtractReferenceProperties();
|
||||
this.ExtractReferencesProperties();
|
||||
}
|
||||
|
||||
private void ExtractReferenceProperties()
|
||||
{
|
||||
foreach (var (entryName, connection) in this.SectionProperties.ReferenceAttributes)
|
||||
{
|
||||
var hasID = this.MaybeExtractIntegerFromParsed(entryName, out var id);
|
||||
if (hasID)
|
||||
{
|
||||
Debug.Assert(this._parsed != null);
|
||||
var found = this._parsed.TryGetValue(key, out var extracted);
|
||||
if (found)
|
||||
{
|
||||
Debug.Assert(extracted != null);
|
||||
value = extracted;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = string.Empty;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private bool MaybeExtractIntegerFromParsed(string key, out int value)
|
||||
{
|
||||
var available = this.MaybeExtractFromParsed(key, out var extracted);
|
||||
if (!available)
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
value = ExtractInteger(extracted);
|
||||
return available;
|
||||
}
|
||||
|
||||
protected bool MaybeExtractCompoundInteger(string key, out List<int> value)
|
||||
{
|
||||
var available = this.MaybeExtractFromParsed(key, out var extracted);
|
||||
if (!available)
|
||||
{
|
||||
value = [];
|
||||
return false;
|
||||
}
|
||||
value = ExtractCompoundInteger(extracted);
|
||||
return available;
|
||||
}
|
||||
|
||||
private void SetProperty(string name, object obj)
|
||||
{
|
||||
var type = this.GetType();
|
||||
var property = type.GetProperty(name);
|
||||
Debug.Assert(property != null);
|
||||
property.SetValue(this, obj);
|
||||
}
|
||||
|
||||
public void ExtractReferences()
|
||||
{
|
||||
this.ExtractReferenceProperties();
|
||||
this.ExtractReferencesProperties();
|
||||
}
|
||||
|
||||
private void ExtractReferenceProperties()
|
||||
{
|
||||
foreach (var (entryName, connection) in this.SectionProperties.ReferenceAttributes)
|
||||
{
|
||||
var hasID = this.MaybeExtractIntegerFromParsed(entryName, out var id);
|
||||
if (hasID)
|
||||
{
|
||||
var (name, type) = connection;
|
||||
this.ExtractReferenceProperty(id, name, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractReferenceProperty(int id, string name, System.Type type)
|
||||
{
|
||||
// The reference container in the parent class
|
||||
var referenceSectionProperties = GetSectionProperties(type);
|
||||
var referenceClassAttribute = referenceSectionProperties.ClassAttribute;
|
||||
Debug.Assert(referenceClassAttribute != null);
|
||||
var referencingContainer = referenceClassAttribute.Referencing;
|
||||
|
||||
// Get the parent container field
|
||||
var containerType = this._container.GetType();
|
||||
Debug.Assert(referencingContainer != null);
|
||||
var containerField = containerType.GetField(referencingContainer);
|
||||
Debug.Assert(containerField != null);
|
||||
var fieldValue = containerField.GetValue(this._container);
|
||||
var fieldList = fieldValue as IList;
|
||||
|
||||
// Now get the referenced object from the parent container field (via ID as an index)
|
||||
Debug.Assert(fieldList != null);
|
||||
var referencingObject = fieldList[id];
|
||||
|
||||
// Now set our reference field
|
||||
Debug.Assert(referencingObject != null);
|
||||
this.SetProperty(name, referencingObject);
|
||||
}
|
||||
|
||||
private void ExtractReferencesProperties()
|
||||
{
|
||||
foreach (var (entryName, connection) in this.SectionProperties.ReferencesAttributes)
|
||||
{
|
||||
// this'll be a set of ids
|
||||
var hasIDs = this.MaybeExtractCompoundInteger(entryName, out var ids);
|
||||
if (hasIDs)
|
||||
{
|
||||
var (name, type) = connection;
|
||||
this.ExtractReferencesProperty(ids, name, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractReferencesProperty(List<int> ids, string name, System.Type type)
|
||||
{
|
||||
// The reference container in the parent class
|
||||
//var referenceSectionProperties = GetSectionProperties(type);
|
||||
//var referenceClassAttribute = referenceSectionProperties.ClassAttribute;
|
||||
//Debug.Assert(referenceClassAttribute != null);
|
||||
//var referencingContainer = referenceClassAttribute.Referencing;
|
||||
|
||||
// Get the parent container field
|
||||
//var containerType = this._container.GetType();
|
||||
//Debug.Assert(referencingContainer != null);
|
||||
//var containerField = containerType.GetField(referencingContainer);
|
||||
//Debug.Assert(containerField != null);
|
||||
//var fieldValue = containerField.GetValue(this._container);
|
||||
//var fieldList = fieldValue as IList;
|
||||
|
||||
// Now get the referenced object from the parent container field (via ID as an index)
|
||||
//Debug.Assert(fieldList != null);
|
||||
//var referencingObject = fieldList[id];
|
||||
|
||||
//// Now set our reference field
|
||||
//Debug.Assert(referencingObject != null);
|
||||
//this.SetProperty(name, referencingObject);
|
||||
var (name, type) = connection;
|
||||
this.ExtractReferenceProperty(id, name, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractReferenceProperty(int id, string name, System.Type type)
|
||||
{
|
||||
// The reference container in the parent class
|
||||
var referenceSectionProperties = GetSectionProperties(type);
|
||||
var referenceClassAttribute = referenceSectionProperties.ClassAttribute;
|
||||
Debug.Assert(referenceClassAttribute != null);
|
||||
var referencingContainer = referenceClassAttribute.Referencing;
|
||||
|
||||
// Get the parent container field
|
||||
var containerType = this._container.GetType();
|
||||
Debug.Assert(referencingContainer != null);
|
||||
var containerField = containerType.GetField(referencingContainer);
|
||||
Debug.Assert(containerField != null);
|
||||
var fieldValue = containerField.GetValue(this._container);
|
||||
var fieldList = fieldValue as IList;
|
||||
|
||||
// Now get the referenced object from the parent container field (via ID as an index)
|
||||
Debug.Assert(fieldList != null);
|
||||
var referencingObject = fieldList[id];
|
||||
|
||||
// Now set our reference field
|
||||
Debug.Assert(referencingObject != null);
|
||||
this.SetProperty(name, referencingObject);
|
||||
}
|
||||
|
||||
private void ExtractReferencesProperties()
|
||||
{
|
||||
foreach (var (entryName, connection) in this.SectionProperties.ReferencesAttributes)
|
||||
{
|
||||
// this'll be a set of ids
|
||||
var hasIDs = this.MaybeExtractCompoundInteger(entryName, out var ids);
|
||||
if (hasIDs)
|
||||
{
|
||||
var (name, type) = connection;
|
||||
this.ExtractReferencesProperty(ids, name, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractReferencesProperty(List<int> ids, string name, System.Type type)
|
||||
{
|
||||
// The reference container in the parent class
|
||||
//var referenceSectionProperties = GetSectionProperties(type);
|
||||
//var referenceClassAttribute = referenceSectionProperties.ClassAttribute;
|
||||
//Debug.Assert(referenceClassAttribute != null);
|
||||
//var referencingContainer = referenceClassAttribute.Referencing;
|
||||
|
||||
// Get the parent container field
|
||||
//var containerType = this._container.GetType();
|
||||
//Debug.Assert(referencingContainer != null);
|
||||
//var containerField = containerType.GetField(referencingContainer);
|
||||
//Debug.Assert(containerField != null);
|
||||
//var fieldValue = containerField.GetValue(this._container);
|
||||
//var fieldList = fieldValue as IList;
|
||||
|
||||
// Now get the referenced object from the parent container field (via ID as an index)
|
||||
//Debug.Assert(fieldList != null);
|
||||
//var referencingObject = fieldList[id];
|
||||
|
||||
//// Now set our reference field
|
||||
//Debug.Assert(referencingObject != null);
|
||||
//this.SetProperty(name, referencingObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,39 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
//info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3
|
||||
[Section("info")]
|
||||
public sealed class Information(Parser container) : Section(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
//info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3
|
||||
[Section("info")]
|
||||
public sealed class Information(Parser container) : Section(container)
|
||||
{
|
||||
[SectionProperty("csym")]
|
||||
public int CSymbol { get; private set; }
|
||||
[SectionProperty("csym")]
|
||||
public int CSymbol { get; private set; }
|
||||
|
||||
[SectionProperty("file")]
|
||||
public int File { get; private set; }
|
||||
[SectionProperty("file")]
|
||||
public int File { get; private set; }
|
||||
|
||||
[SectionProperty("lib")]
|
||||
public int Library { get; private set; }
|
||||
[SectionProperty("lib")]
|
||||
public int Library { get; private set; }
|
||||
|
||||
[SectionProperty("line")]
|
||||
public int Line { get; private set; }
|
||||
[SectionProperty("line")]
|
||||
public int Line { get; private set; }
|
||||
|
||||
[SectionProperty("mod")]
|
||||
public int Module { get; private set; }
|
||||
[SectionProperty("mod")]
|
||||
public int Module { get; private set; }
|
||||
|
||||
[SectionProperty("scope")]
|
||||
public int Scope { get; private set; }
|
||||
[SectionProperty("scope")]
|
||||
public int Scope { get; private set; }
|
||||
|
||||
[SectionProperty("seg")]
|
||||
public int Segment { get; private set; }
|
||||
[SectionProperty("seg")]
|
||||
public int Segment { get; private set; }
|
||||
|
||||
[SectionProperty("span")]
|
||||
public int Span { get; private set; }
|
||||
[SectionProperty("span")]
|
||||
public int Span { get; private set; }
|
||||
|
||||
[SectionProperty("sym")]
|
||||
public int Symbol { get; private set; }
|
||||
[SectionProperty("sym")]
|
||||
public int Symbol { get; private set; }
|
||||
|
||||
[SectionProperty("type")]
|
||||
public int Type { get; private set; }
|
||||
[SectionProperty("type")]
|
||||
public int Type { get; private set; }
|
||||
|
||||
public int Count(string key) => this.GetValueT<int>(key);
|
||||
}
|
||||
}
|
||||
public int Count(string key) => this.GetValueT<int>(key);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,22 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
// line id = 268, file = 1, line = 60, type = 2, count = 1, span = 286 + 195
|
||||
[Section("line", "Lines")]
|
||||
public sealed class Line(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
// line id = 268, file = 1, line = 60, type = 2, count = 1, span = 286 + 195
|
||||
[Section("line", "Lines")]
|
||||
public sealed class Line(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
[SectionReference("file")]
|
||||
public Symbols.File? File { get; private set; }
|
||||
[SectionReference("file")]
|
||||
public Symbols.File? File { get; private set; }
|
||||
|
||||
[SectionProperty("line")]
|
||||
public int LineNumber { get; private set; }
|
||||
[SectionProperty("line")]
|
||||
public int LineNumber { get; private set; }
|
||||
|
||||
[SectionReference("type")]
|
||||
public Symbols.Type? Type { get; private set; }
|
||||
[SectionReference("type")]
|
||||
public Symbols.Type? Type { get; private set; }
|
||||
|
||||
[SectionProperty("count")]
|
||||
public int? Count { get; private set; }
|
||||
[SectionProperty("count")]
|
||||
public int? Count { get; private set; }
|
||||
|
||||
[SectionReferences("span")]
|
||||
public List<Span>? Spans { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionReferences("span")]
|
||||
public List<Span>? Spans { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
// mod id=0,name="sudoku.o",file=0
|
||||
[Section("mod", "Modules")]
|
||||
public sealed class Module(Parser container) : NamedSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
// mod id=0,name="sudoku.o",file=0
|
||||
[Section("mod", "Modules")]
|
||||
public sealed class Module(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionReference("file")]
|
||||
public Symbols.File? File { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionReference("file")]
|
||||
public Symbols.File? File { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,11 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
public class NamedSection : IdentifiableSection
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
public class NamedSection : IdentifiableSection
|
||||
{
|
||||
[SectionProperty("name")]
|
||||
public string? Name { get; protected set; }
|
||||
[SectionProperty("name")]
|
||||
public string? Name { get; protected set; }
|
||||
|
||||
protected NamedSection(Parser container)
|
||||
: base(container) { }
|
||||
}
|
||||
}
|
||||
protected NamedSection(Parser container)
|
||||
: base(container) { }
|
||||
}
|
||||
}
|
||||
@@ -1,428 +1,422 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public sealed class Parser
|
||||
{
|
||||
namespace Symbols
|
||||
#region Variables, properties etc.
|
||||
|
||||
private Version? _version;
|
||||
private Information? _information;
|
||||
|
||||
// Section -> Unique ID list of dictionary entries
|
||||
public Dictionary<string, List<Dictionary<string, string>>> Parsed { get; } = [];
|
||||
|
||||
public List<Section> SectionEntries { get; } = [];
|
||||
|
||||
public List<File> Files = [];
|
||||
|
||||
public List<Line> Lines = [];
|
||||
|
||||
public List<Module> Modules = [];
|
||||
|
||||
public List<Segment> Segments = [];
|
||||
|
||||
public List<Span> Spans = [];
|
||||
|
||||
public List<Scope> Scopes = [];
|
||||
|
||||
public List<Symbol> Symbols = [];
|
||||
|
||||
public List<Type> Types = [];
|
||||
|
||||
|
||||
// Symbol sub-types
|
||||
public IEnumerable<Symbol> Labels => this.SelectSymbolsByType("lab");
|
||||
public IEnumerable<Symbol> Equates => this.SelectSymbolsByType("equ");
|
||||
|
||||
// Scope clarification
|
||||
public List<Scope> AddressableScopes = [];
|
||||
|
||||
// Scope cache for precomputed ranges
|
||||
private readonly int?[] _scopeAddressCache = new int?[0x10000];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lookups
|
||||
|
||||
private static IEnumerable<T> SelectIdMatching<T>(int id, IEnumerable<T>? items) where T : IdentifiableSection => from item in items where item.ID == id select item;
|
||||
|
||||
private IEnumerable<Symbol> SelectSymbolsByType(string type) => from symbol in this.Symbols where symbol.Type == type select symbol;
|
||||
|
||||
#region Label lookup
|
||||
|
||||
private IEnumerable<Symbol> LookupLabelsByAddress(int address) => from label in this.Labels where label.Value == address select label;
|
||||
|
||||
public Symbol? LookupLabelByAddress(int address)
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
var labels = this.LookupLabelsByAddress(address).ToList();
|
||||
return labels.Count > 0 ? labels[0] : null;
|
||||
}
|
||||
|
||||
public sealed class Parser
|
||||
private IEnumerable<Symbol> LookupLabelsByID(int id) => SelectIdMatching(id, this.Labels);
|
||||
|
||||
public Symbol? LookupLabelByID(int id)
|
||||
{
|
||||
var labels = this.LookupLabelsByID(id).ToList();
|
||||
return labels.Count > 0 ? labels[0] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constant lookup
|
||||
|
||||
private IEnumerable<Symbol> LookupEquatesByValue(int constant) => from equate in this.Equates where equate.Value == constant select equate;
|
||||
|
||||
public Symbol? LookupEquateByValue(int constant)
|
||||
{
|
||||
var equates = this.LookupEquatesByValue(constant).ToList();
|
||||
return equates.Count > 0 ? equates[0] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scope lookup
|
||||
|
||||
private int LocateScopeByAddress(int address)
|
||||
{
|
||||
var low = 0;
|
||||
var high = this.AddressableScopes.Count - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
#region Variables, properties etc.
|
||||
var mid = low + ((high - low) / 2);
|
||||
|
||||
private Version? _version;
|
||||
private Information? _information;
|
||||
var scope = this.AddressableScopes[mid];
|
||||
|
||||
// Section -> Unique ID list of dictionary entries
|
||||
public Dictionary<string, List<Dictionary<string, string>>> Parsed { get; } = [];
|
||||
var symbol = scope.Symbol;
|
||||
Debug.Assert(symbol != null);
|
||||
var start = symbol.Value;
|
||||
|
||||
public List<Section> SectionEntries { get; } = [];
|
||||
|
||||
public List<File> Files = [];
|
||||
|
||||
public List<Line> Lines = [];
|
||||
|
||||
public List<Module> Modules = [];
|
||||
|
||||
public List<Segment> Segments = [];
|
||||
|
||||
public List<Span> Spans = [];
|
||||
|
||||
public List<Scope> Scopes = [];
|
||||
|
||||
public List<Symbol> Symbols = [];
|
||||
|
||||
public List<Type> Types = [];
|
||||
|
||||
|
||||
// Symbol sub-types
|
||||
public IEnumerable<Symbol> Labels => this.SelectSymbolsByType("lab");
|
||||
public IEnumerable<Symbol> Equates => this.SelectSymbolsByType("equ");
|
||||
|
||||
// Scope clarification
|
||||
public List<Scope> AddressableScopes = [];
|
||||
|
||||
// Scope cache for precomputed ranges
|
||||
private readonly int?[] _scopeAddressCache = new int?[0x10000];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lookups
|
||||
|
||||
private static IEnumerable<T> SelectIdMatching<T>(int id, IEnumerable<T>? items) where T : IdentifiableSection => from item in items where item.ID == id select item;
|
||||
|
||||
private IEnumerable<Symbol> SelectSymbolsByType(string type) => from symbol in this.Symbols where symbol.Type == type select symbol;
|
||||
|
||||
#region Label lookup
|
||||
|
||||
private IEnumerable<Symbol> LookupLabelsByAddress(int address) => from label in this.Labels where label.Value == address select label;
|
||||
|
||||
public Symbol? LookupLabelByAddress(int address)
|
||||
if (address < start)
|
||||
{
|
||||
var labels = this.LookupLabelsByAddress(address).ToList();
|
||||
return labels.Count > 0 ? labels[0] : null;
|
||||
high = mid - 1;
|
||||
}
|
||||
|
||||
private IEnumerable<Symbol> LookupLabelsByID(int id) => SelectIdMatching(id, this.Labels);
|
||||
|
||||
public Symbol? LookupLabelByID(int id)
|
||||
else
|
||||
{
|
||||
var labels = this.LookupLabelsByID(id).ToList();
|
||||
return labels.Count > 0 ? labels[0] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constant lookup
|
||||
|
||||
private IEnumerable<Symbol> LookupEquatesByValue(int constant) => from equate in this.Equates where equate.Value == constant select equate;
|
||||
|
||||
public Symbol? LookupEquateByValue(int constant)
|
||||
{
|
||||
var equates = this.LookupEquatesByValue(constant).ToList();
|
||||
return equates.Count > 0 ? equates[0] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scope lookup
|
||||
|
||||
private int LocateScopeByAddress(int address)
|
||||
{
|
||||
var low = 0;
|
||||
var high = this.AddressableScopes.Count - 1;
|
||||
|
||||
while (low <= high)
|
||||
var end = start + scope.Size;
|
||||
if (address >= end)
|
||||
{
|
||||
var mid = low + ((high - low) / 2);
|
||||
|
||||
var scope = this.AddressableScopes[mid];
|
||||
|
||||
var symbol = scope.Symbol;
|
||||
Debug.Assert(symbol != null);
|
||||
var start = symbol.Value;
|
||||
|
||||
if (address < start)
|
||||
{
|
||||
high = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var end = start + scope.Size;
|
||||
if (address >= end)
|
||||
{
|
||||
low = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, then scope was not present
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Scope? LookupScopeByAddress(int address)
|
||||
{
|
||||
var index = _scopeAddressCache[address] ?? (_scopeAddressCache[address] = this.LocateScopeByAddress(address));
|
||||
return index == -1 ? null : this.AddressableScopes[index.Value];
|
||||
}
|
||||
|
||||
private IEnumerable<Scope> LookupScopesByID(int id) => SelectIdMatching(id, this.Scopes);
|
||||
|
||||
public Scope? LookupScopeByID(int id)
|
||||
{
|
||||
var scopes = this.LookupScopesByID(id).ToList();
|
||||
return scopes.Count > 0 ? scopes[0] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scope evaluation
|
||||
|
||||
private static List<Scope> EvaluateScope(Scope? start)
|
||||
{
|
||||
Debug.Assert(start != null);
|
||||
var returned = new List<Scope>();
|
||||
for (var current = start; current.Parent != null; current = current.Parent)
|
||||
{
|
||||
returned.Add(current);
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
|
||||
private static List<Scope> EvaluateScope(Symbol symbol) => EvaluateScope(symbol.Scope);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace evaluation from scope
|
||||
|
||||
private static string BuildNamespace(Symbol symbol)
|
||||
{
|
||||
var returned = string.Empty;
|
||||
var scopes = EvaluateScope(symbol);
|
||||
for (var i = scopes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var scope = scopes[i];
|
||||
var name = scope.Name;
|
||||
Debug.Assert(!string.IsNullOrEmpty(name));
|
||||
returned += name;
|
||||
var last = i == 0;
|
||||
if (!last)
|
||||
{
|
||||
returned += '.';
|
||||
}
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
|
||||
private static string PrefixNamespace(Symbol? symbol)
|
||||
{
|
||||
if (symbol is null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var prefix = BuildNamespace(symbol);
|
||||
var name = symbol.Name;
|
||||
Debug.Assert(!string.IsNullOrEmpty(name));
|
||||
return string.IsNullOrEmpty(prefix) ? name : $"{prefix}.{name}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Qualified symbol lookup
|
||||
|
||||
public bool TryGetQualifiedLabelByAddress(ushort absolute, out string label)
|
||||
{
|
||||
var symbol = this.LookupLabelByAddress(absolute);
|
||||
label = PrefixNamespace(symbol);
|
||||
return symbol != null;
|
||||
}
|
||||
|
||||
public string MaybeGetQualifiedLabelByAddress(ushort absolute) => this.TryGetQualifiedLabelByAddress(absolute, out var label) ? label : string.Empty;
|
||||
|
||||
public bool TryGetQualifiedEquateValue(ushort value, out string name)
|
||||
{
|
||||
var symbol = this.LookupEquateByValue(value);
|
||||
name = PrefixNamespace(symbol);
|
||||
return symbol != null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Section extractors
|
||||
|
||||
private void ExtractSections()
|
||||
{
|
||||
this.ExtractFiles();
|
||||
this.ExtractLines();
|
||||
this.ExtractModules();
|
||||
this.ExtractSegments();
|
||||
this.ExtractSpans();
|
||||
this.ExtractScopes();
|
||||
this.ExtractSymbols();
|
||||
this.ExtractTypes();
|
||||
}
|
||||
|
||||
private void ExtractFiles() => this.Extract<File>("file", this.Files);
|
||||
|
||||
private void ExtractLines() => this.Extract<Line>("line", this.Lines);
|
||||
|
||||
private void ExtractModules() => this.Extract<Module>("mod", this.Modules);
|
||||
|
||||
private void ExtractSegments() => this.Extract<Segment>("seg", this.Segments);
|
||||
|
||||
private void ExtractSpans() => this.Extract<Span>("span", this.Spans);
|
||||
|
||||
private void ExtractScopes() => this.Extract<Scope>("scope", this.Scopes);
|
||||
|
||||
private void ExtractSymbols() => this.Extract<Symbol>("sym", this.Symbols);
|
||||
|
||||
private void ExtractTypes() => this.Extract<Type>("type", this.Types);
|
||||
|
||||
private void Extract<T>(string key, List<T> into) where T : IdentifiableSection
|
||||
{
|
||||
if (!this.Parsed.TryGetValue(key, out var parsed))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(key), key, "Debugging section is unavailable");
|
||||
}
|
||||
this.ExtractIdentifiableSection(key, into, parsed);
|
||||
}
|
||||
|
||||
private void ExtractIdentifiableSection<T>(string key, List<T> into, List<Dictionary<string, string>> parsed) where T : IdentifiableSection
|
||||
{
|
||||
into.Capacity = parsed.Count;
|
||||
for (var id = 0; id < parsed.Count; ++id)
|
||||
{
|
||||
this.ExtractIdentifiableEntry(id, parsed, into);
|
||||
}
|
||||
Debug.Assert(into.Count == parsed.Count);
|
||||
this.VerifyInformationCount(key, into.Count);
|
||||
}
|
||||
|
||||
private void ExtractIdentifiableEntry<T>(int id, List<Dictionary<string, string>> parsed, List<T> into) where T : IdentifiableSection
|
||||
{
|
||||
Debug.Assert(into.Count == id);
|
||||
var information = parsed[id];
|
||||
var entry = this.ExtractEntry<T>(information);
|
||||
Debug.Assert(into.Capacity > id);
|
||||
into.Add(entry);
|
||||
}
|
||||
|
||||
private T ExtractEntry<T>(Dictionary<string, string> information) where T : Section
|
||||
{
|
||||
var entry = (T?)Activator.CreateInstance(typeof(T), this);
|
||||
Debug.Assert(entry != null);
|
||||
entry.Parse(information);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void VerifyInformationCount(string key, int actual)
|
||||
{
|
||||
Debug.Assert(this._information != null);
|
||||
var expected = this._information?.Count(key);
|
||||
if (expected != actual)
|
||||
{
|
||||
throw new InvalidOperationException($"information count mismatch for {key}. Expected {expected}, actual {actual}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ExtractReferences()
|
||||
{
|
||||
foreach (var entry in this.SectionEntries)
|
||||
{
|
||||
var identifiableEntry = entry as IdentifiableSection;
|
||||
identifiableEntry?.ExtractReferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildAddressableScopes()
|
||||
{
|
||||
var scopes = from scope in this.Scopes where scope.Symbol != null select scope;
|
||||
this.AddressableScopes.AddRange(scopes);
|
||||
}
|
||||
|
||||
#region Parser driver
|
||||
|
||||
public async Task ParseAsync(string? path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(path);
|
||||
await this.ParseAsync(reader);
|
||||
|
||||
this.ExtractSections();
|
||||
this.ExtractReferences();
|
||||
|
||||
this.Parsed.Clear();
|
||||
|
||||
this.BuildAddressableScopes();
|
||||
}
|
||||
|
||||
private async Task ParseAsync(StreamReader reader)
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = await reader.ReadLineAsync();
|
||||
if (line == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
this.ParseLine(line.Split(' ', '\t'));
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseLine(string[] elements)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(elements, nameof(elements));
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(elements.Length, 2, nameof(elements));
|
||||
|
||||
var key = elements[0];
|
||||
var parts = elements[1].Split(',');
|
||||
|
||||
if (key is "version")
|
||||
{
|
||||
if (this._version != null)
|
||||
{
|
||||
throw new InvalidOperationException("Verson object has already been parsed");
|
||||
}
|
||||
this._version = new(this);
|
||||
this._version.Parse(BuildDictionary(parts));
|
||||
this.VerifyVersion();
|
||||
}
|
||||
else if (key is "info")
|
||||
{
|
||||
if (this._information != null)
|
||||
{
|
||||
throw new InvalidOperationException("Information object has already been parsed");
|
||||
}
|
||||
this._information = new(this);
|
||||
this._information.Parse(BuildDictionary(parts));
|
||||
low = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Parse(key, parts);
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Parse(string key, string[] parts)
|
||||
// If we reach here, then scope was not present
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Scope? LookupScopeByAddress(int address)
|
||||
{
|
||||
var index = _scopeAddressCache[address] ?? (_scopeAddressCache[address] = this.LocateScopeByAddress(address));
|
||||
return index == -1 ? null : this.AddressableScopes[index.Value];
|
||||
}
|
||||
|
||||
private IEnumerable<Scope> LookupScopesByID(int id) => SelectIdMatching(id, this.Scopes);
|
||||
|
||||
public Scope? LookupScopeByID(int id)
|
||||
{
|
||||
var scopes = this.LookupScopesByID(id).ToList();
|
||||
return scopes.Count > 0 ? scopes[0] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scope evaluation
|
||||
|
||||
private static List<Scope> EvaluateScope(Scope? start)
|
||||
{
|
||||
Debug.Assert(start != null);
|
||||
var returned = new List<Scope>();
|
||||
for (var current = start; current.Parent != null; current = current.Parent)
|
||||
{
|
||||
returned.Add(current);
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
|
||||
private static List<Scope> EvaluateScope(Symbol symbol) => EvaluateScope(symbol.Scope);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace evaluation from scope
|
||||
|
||||
private static string BuildNamespace(Symbol symbol)
|
||||
{
|
||||
var returned = string.Empty;
|
||||
var scopes = EvaluateScope(symbol);
|
||||
for (var i = scopes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var scope = scopes[i];
|
||||
var name = scope.Name;
|
||||
Debug.Assert(!string.IsNullOrEmpty(name));
|
||||
returned += name;
|
||||
var last = i == 0;
|
||||
if (!last)
|
||||
{
|
||||
if (!this.Parsed.TryGetValue(key, out var section))
|
||||
{
|
||||
this.Parsed.Add(key, section = []);
|
||||
}
|
||||
section.Add(BuildDictionary(parts));
|
||||
returned += '.';
|
||||
}
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> BuildDictionary(string[] parts)
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>(parts.Length);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var definition = part.Split('=');
|
||||
if (definition.Length != 2)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid symbol file format (definition is missing equals)");
|
||||
}
|
||||
private static string PrefixNamespace(Symbol? symbol)
|
||||
{
|
||||
if (symbol is null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var prefix = BuildNamespace(symbol);
|
||||
var name = symbol.Name;
|
||||
Debug.Assert(!string.IsNullOrEmpty(name));
|
||||
return string.IsNullOrEmpty(prefix) ? name : $"{prefix}.{name}";
|
||||
}
|
||||
|
||||
var key = definition[0];
|
||||
var value = definition[1];
|
||||
dictionary.Add(key, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
#region Qualified symbol lookup
|
||||
|
||||
private void VerifyVersion()
|
||||
{
|
||||
if (this._version == null)
|
||||
{
|
||||
throw new InvalidOperationException("Version has not yet been parsed");
|
||||
}
|
||||
public bool TryGetQualifiedLabelByAddress(ushort absolute, out string label)
|
||||
{
|
||||
var symbol = this.LookupLabelByAddress(absolute);
|
||||
label = PrefixNamespace(symbol);
|
||||
return symbol != null;
|
||||
}
|
||||
|
||||
var major = this._version?.Major;
|
||||
var minor = this._version?.Minor;
|
||||
var valid = major == 2 && minor == 0;
|
||||
if (!valid)
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown symbol file version: {major}.{minor}");
|
||||
}
|
||||
}
|
||||
public string MaybeGetQualifiedLabelByAddress(ushort absolute) => this.TryGetQualifiedLabelByAddress(absolute, out var label) ? label : string.Empty;
|
||||
|
||||
#endregion
|
||||
public bool TryGetQualifiedEquateValue(ushort value, out string name)
|
||||
{
|
||||
var symbol = this.LookupEquateByValue(value);
|
||||
name = PrefixNamespace(symbol);
|
||||
return symbol != null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Section extractors
|
||||
|
||||
private void ExtractSections()
|
||||
{
|
||||
this.ExtractFiles();
|
||||
this.ExtractLines();
|
||||
this.ExtractModules();
|
||||
this.ExtractSegments();
|
||||
this.ExtractSpans();
|
||||
this.ExtractScopes();
|
||||
this.ExtractSymbols();
|
||||
this.ExtractTypes();
|
||||
}
|
||||
|
||||
private void ExtractFiles() => this.Extract<File>("file", this.Files);
|
||||
|
||||
private void ExtractLines() => this.Extract<Line>("line", this.Lines);
|
||||
|
||||
private void ExtractModules() => this.Extract<Module>("mod", this.Modules);
|
||||
|
||||
private void ExtractSegments() => this.Extract<Segment>("seg", this.Segments);
|
||||
|
||||
private void ExtractSpans() => this.Extract<Span>("span", this.Spans);
|
||||
|
||||
private void ExtractScopes() => this.Extract<Scope>("scope", this.Scopes);
|
||||
|
||||
private void ExtractSymbols() => this.Extract<Symbol>("sym", this.Symbols);
|
||||
|
||||
private void ExtractTypes() => this.Extract<Type>("type", this.Types);
|
||||
|
||||
private void Extract<T>(string key, List<T> into) where T : IdentifiableSection
|
||||
{
|
||||
if (!this.Parsed.TryGetValue(key, out var parsed))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(key), key, "Debugging section is unavailable");
|
||||
}
|
||||
this.ExtractIdentifiableSection(key, into, parsed);
|
||||
}
|
||||
|
||||
private void ExtractIdentifiableSection<T>(string key, List<T> into, List<Dictionary<string, string>> parsed) where T : IdentifiableSection
|
||||
{
|
||||
into.Capacity = parsed.Count;
|
||||
for (var id = 0; id < parsed.Count; ++id)
|
||||
{
|
||||
this.ExtractIdentifiableEntry(id, parsed, into);
|
||||
}
|
||||
Debug.Assert(into.Count == parsed.Count);
|
||||
this.VerifyInformationCount(key, into.Count);
|
||||
}
|
||||
|
||||
private void ExtractIdentifiableEntry<T>(int id, List<Dictionary<string, string>> parsed, List<T> into) where T : IdentifiableSection
|
||||
{
|
||||
Debug.Assert(into.Count == id);
|
||||
var information = parsed[id];
|
||||
var entry = this.ExtractEntry<T>(information);
|
||||
Debug.Assert(into.Capacity > id);
|
||||
into.Add(entry);
|
||||
}
|
||||
|
||||
private T ExtractEntry<T>(Dictionary<string, string> information) where T : Section
|
||||
{
|
||||
var entry = (T?)Activator.CreateInstance(typeof(T), this);
|
||||
Debug.Assert(entry != null);
|
||||
entry.Parse(information);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void VerifyInformationCount(string key, int actual)
|
||||
{
|
||||
Debug.Assert(this._information != null);
|
||||
var expected = this._information?.Count(key);
|
||||
if (expected != actual)
|
||||
{
|
||||
throw new InvalidOperationException($"information count mismatch for {key}. Expected {expected}, actual {actual}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ExtractReferences()
|
||||
{
|
||||
foreach (var entry in this.SectionEntries)
|
||||
{
|
||||
var identifiableEntry = entry as IdentifiableSection;
|
||||
identifiableEntry?.ExtractReferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildAddressableScopes()
|
||||
{
|
||||
var scopes = from scope in this.Scopes where scope.Symbol != null select scope;
|
||||
this.AddressableScopes.AddRange(scopes);
|
||||
}
|
||||
|
||||
#region Parser driver
|
||||
|
||||
public async Task ParseAsync(string? path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(path);
|
||||
await this.ParseAsync(reader);
|
||||
|
||||
this.ExtractSections();
|
||||
this.ExtractReferences();
|
||||
|
||||
this.Parsed.Clear();
|
||||
|
||||
this.BuildAddressableScopes();
|
||||
}
|
||||
|
||||
private async Task ParseAsync(StreamReader reader)
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = await reader.ReadLineAsync();
|
||||
if (line == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
this.ParseLine(line.Split(' ', '\t'));
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseLine(string[] elements)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(elements, nameof(elements));
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(elements.Length, 2, nameof(elements));
|
||||
|
||||
var key = elements[0];
|
||||
var parts = elements[1].Split(',');
|
||||
|
||||
if (key is "version")
|
||||
{
|
||||
if (this._version != null)
|
||||
{
|
||||
throw new InvalidOperationException("Verson object has already been parsed");
|
||||
}
|
||||
this._version = new(this);
|
||||
this._version.Parse(BuildDictionary(parts));
|
||||
this.VerifyVersion();
|
||||
}
|
||||
else if (key is "info")
|
||||
{
|
||||
if (this._information != null)
|
||||
{
|
||||
throw new InvalidOperationException("Information object has already been parsed");
|
||||
}
|
||||
this._information = new(this);
|
||||
this._information.Parse(BuildDictionary(parts));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Parse(key, parts);
|
||||
}
|
||||
}
|
||||
|
||||
private void Parse(string key, string[] parts)
|
||||
{
|
||||
if (!this.Parsed.TryGetValue(key, out var section))
|
||||
{
|
||||
this.Parsed.Add(key, section = []);
|
||||
}
|
||||
section.Add(BuildDictionary(parts));
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> BuildDictionary(string[] parts)
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>(parts.Length);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var definition = part.Split('=');
|
||||
if (definition.Length != 2)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid symbol file format (definition is missing equals)");
|
||||
}
|
||||
|
||||
var key = definition[0];
|
||||
var value = definition[1];
|
||||
dictionary.Add(key, value);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
private void VerifyVersion()
|
||||
{
|
||||
if (this._version == null)
|
||||
{
|
||||
throw new InvalidOperationException("Version has not yet been parsed");
|
||||
}
|
||||
|
||||
var major = this._version?.Major;
|
||||
var minor = this._version?.Minor;
|
||||
var valid = major == 2 && minor == 0;
|
||||
if (!valid)
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown symbol file version: {major}.{minor}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,226 +1,220 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
public sealed class ReflectedSectionProperties
|
||||
{
|
||||
namespace Symbols
|
||||
private readonly Dictionary<string, string> _entriesToProperties = [];
|
||||
|
||||
public HashSet<string> StringKeys { get; } = [];
|
||||
public HashSet<string> EnumerationKeys { get; } = [];
|
||||
public HashSet<string> IntegerKeys { get; } = [];
|
||||
public HashSet<string> HexIntegerKeys { get; } = [];
|
||||
public HashSet<string> LongKeys { get; } = [];
|
||||
public HashSet<string> HexLongKeys { get; } = [];
|
||||
public HashSet<string> DateTimeKeys { get; } = [];
|
||||
public HashSet<string> MultipleKeys { get; } = [];
|
||||
|
||||
public Dictionary<string, PropertyInfo> Properties { get; } = [];
|
||||
|
||||
internal SectionAttribute? ClassAttribute { get; private set; }
|
||||
|
||||
internal Dictionary<string, Tuple<string, System.Type>> ReferenceAttributes { get; } = [];
|
||||
|
||||
internal Dictionary<string, Tuple<string, System.Type>> ReferencesAttributes { get; } = [];
|
||||
|
||||
public void Build(System.Type type)
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
var sectionAttributes = type.GetCustomAttributes(typeof(SectionAttribute), true);
|
||||
Debug.Assert(sectionAttributes != null, "No section attributes available");
|
||||
Debug.Assert(sectionAttributes.Length == 1, "Must be a single section attribute available");
|
||||
var sectionAttribute = sectionAttributes[0];
|
||||
Debug.Assert(sectionAttribute != null, "Section attribute cannot be null");
|
||||
this.ClassAttribute = (SectionAttribute)sectionAttribute;
|
||||
|
||||
public sealed class ReflectedSectionProperties
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
private readonly Dictionary<string, string> _entriesToProperties = [];
|
||||
this.ProcessPropertyAttributes(property);
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<string> StringKeys { get; } = [];
|
||||
public HashSet<string> EnumerationKeys { get; } = [];
|
||||
public HashSet<string> IntegerKeys { get; } = [];
|
||||
public HashSet<string> HexIntegerKeys { get; } = [];
|
||||
public HashSet<string> LongKeys { get; } = [];
|
||||
public HashSet<string> HexLongKeys { get; } = [];
|
||||
public HashSet<string> DateTimeKeys { get; } = [];
|
||||
public HashSet<string> MultipleKeys { get; } = [];
|
||||
public string GetPropertyNameFromEntryName(string name)
|
||||
{
|
||||
var found = this._entriesToProperties.TryGetValue(name, out var propertyName);
|
||||
if (!found)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(name), name, "Missing property mapping");
|
||||
}
|
||||
Debug.Assert(propertyName != null);
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public Dictionary<string, PropertyInfo> Properties { get; } = [];
|
||||
private PropertyInfo GetPropertyFromEntryName(string key)
|
||||
{
|
||||
var propertyName = this.GetPropertyNameFromEntryName(key);
|
||||
var propertyAvailable = this.Properties.TryGetValue(propertyName, out var property);
|
||||
Debug.Assert(propertyAvailable);
|
||||
Debug.Assert(property != null);
|
||||
return property;
|
||||
}
|
||||
|
||||
internal SectionAttribute? ClassAttribute { get; private set; }
|
||||
public object? GetValue(object? obj, string key)
|
||||
{
|
||||
var property = this.GetPropertyFromEntryName(key);
|
||||
Debug.Assert(property.CanRead);
|
||||
return property.GetValue(obj);
|
||||
}
|
||||
|
||||
internal Dictionary<string, Tuple<string, System.Type>> ReferenceAttributes { get; } = [];
|
||||
public void SetValue(object? obj, string key, object? value)
|
||||
{
|
||||
var property = this.GetPropertyFromEntryName(key);
|
||||
Debug.Assert(property.CanWrite);
|
||||
property.SetValue(obj, value); ;
|
||||
}
|
||||
|
||||
internal Dictionary<string, Tuple<string, System.Type>> ReferencesAttributes { get; } = [];
|
||||
public T? MaybeGetValue<T>(object? obj, string key) => (T?)this.GetValue(obj, key);
|
||||
|
||||
public void Build(System.Type type)
|
||||
public T GetValueT<T>(object? obj, string key)
|
||||
{
|
||||
var possible = this.MaybeGetValue<T>(obj, key);
|
||||
return possible != null ? possible : throw new ArgumentOutOfRangeException(nameof(key), key, "Property read issue");
|
||||
}
|
||||
|
||||
private void ProcessPropertyAttributes(PropertyInfo property)
|
||||
{
|
||||
var attributes = property.GetCustomAttributes(typeof(SectionPropertyAttribute), true);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
Debug.Assert(attributes.Length == 1, "Too many section property attributes");
|
||||
this.Properties.Add(property.Name, property);
|
||||
this.ProcessSectionPropertyAttribute(property.PropertyType, property.Name, attributes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessSectionPropertyAttribute(System.Type? type, string name, object attribute)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(type, nameof(type));
|
||||
this.ProcessSectionPropertyAttribute(type, name, (SectionPropertyAttribute)attribute);
|
||||
}
|
||||
|
||||
public void AddStringKey(string key)
|
||||
{
|
||||
var added = this.StringKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddEnumerationKey(string key)
|
||||
{
|
||||
var added = this.EnumerationKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddMultiplesKey(string key)
|
||||
{
|
||||
var added = this.MultipleKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddHexIntegerKey(string key)
|
||||
{
|
||||
var added = this.HexIntegerKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddIntegerKey(string key)
|
||||
{
|
||||
var added = this.IntegerKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddHexLongKey(string key)
|
||||
{
|
||||
var added = this.HexLongKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddLongKey(string key)
|
||||
{
|
||||
var added = this.LongKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddDateTimeKey(string key)
|
||||
{
|
||||
var added = this.DateTimeKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
private void ProcessSectionPropertyAttribute(System.Type originalType, string name, SectionPropertyAttribute attribute)
|
||||
{
|
||||
var key = attribute.Key;
|
||||
this._entriesToProperties.Add(key, name);
|
||||
|
||||
if (attribute is SectionReferenceAttribute)
|
||||
{
|
||||
this.ReferenceAttributes.Add(key, new Tuple<string, System.Type>(name, originalType));
|
||||
} else if (attribute is SectionReferencesAttribute referencesAttribute)
|
||||
{
|
||||
this.ReferencesAttributes.Add(key, new Tuple<string, System.Type>(name, originalType));
|
||||
}
|
||||
|
||||
var multiples = attribute.Many;
|
||||
if (multiples)
|
||||
{
|
||||
// Type is irrelevant
|
||||
this.AddMultiplesKey(key);
|
||||
return;
|
||||
}
|
||||
|
||||
var type = attribute.Type ?? originalType;
|
||||
|
||||
var enumeration = attribute.Enumeration;
|
||||
if (enumeration)
|
||||
{
|
||||
Debug.Assert(type == typeof(string), "Enumeration must be of type string");
|
||||
this.AddEnumerationKey(key);
|
||||
return;
|
||||
}
|
||||
|
||||
var hex = attribute.Hexadecimal;
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
Debug.Assert(!enumeration, "Enumeration case should already have been handled");
|
||||
Debug.Assert(!hex, "Cannot have a hexadecimal string type");
|
||||
this.AddStringKey(key);
|
||||
}
|
||||
else if (type == typeof(int) || type == typeof(Nullable<int>))
|
||||
{
|
||||
if (hex)
|
||||
{
|
||||
var sectionAttributes = type.GetCustomAttributes(typeof(SectionAttribute), true);
|
||||
Debug.Assert(sectionAttributes != null, "No section attributes available");
|
||||
Debug.Assert(sectionAttributes.Length == 1, "Must be a single section attribute available");
|
||||
var sectionAttribute = sectionAttributes[0];
|
||||
Debug.Assert(sectionAttribute != null, "Section attribute cannot be null");
|
||||
this.ClassAttribute = (SectionAttribute)sectionAttribute;
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
this.ProcessPropertyAttributes(property);
|
||||
}
|
||||
this.AddHexIntegerKey(key);
|
||||
}
|
||||
|
||||
public string GetPropertyNameFromEntryName(string name)
|
||||
else
|
||||
{
|
||||
var found = this._entriesToProperties.TryGetValue(name, out var propertyName);
|
||||
if (!found)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(name), name, "Missing property mapping");
|
||||
}
|
||||
Debug.Assert(propertyName != null);
|
||||
return propertyName;
|
||||
this.AddIntegerKey(key);
|
||||
}
|
||||
|
||||
private PropertyInfo GetPropertyFromEntryName(string key)
|
||||
}
|
||||
else if (type == typeof(long) || type == typeof(Nullable<long>))
|
||||
{
|
||||
if (hex)
|
||||
{
|
||||
var propertyName = this.GetPropertyNameFromEntryName(key);
|
||||
var propertyAvailable = this.Properties.TryGetValue(propertyName, out var property);
|
||||
Debug.Assert(propertyAvailable);
|
||||
Debug.Assert(property != null);
|
||||
return property;
|
||||
this.AddHexLongKey(key);
|
||||
}
|
||||
|
||||
public object? GetValue(object? obj, string key)
|
||||
else
|
||||
{
|
||||
var property = this.GetPropertyFromEntryName(key);
|
||||
Debug.Assert(property.CanRead);
|
||||
return property.GetValue(obj);
|
||||
}
|
||||
|
||||
public void SetValue(object? obj, string key, object? value)
|
||||
{
|
||||
var property = this.GetPropertyFromEntryName(key);
|
||||
Debug.Assert(property.CanWrite);
|
||||
property.SetValue(obj, value); ;
|
||||
}
|
||||
|
||||
public T? MaybeGetValue<T>(object? obj, string key) => (T?)this.GetValue(obj, key);
|
||||
|
||||
public T GetValueT<T>(object? obj, string key)
|
||||
{
|
||||
var possible = this.MaybeGetValue<T>(obj, key);
|
||||
return possible != null ? possible : throw new ArgumentOutOfRangeException(nameof(key), key, "Property read issue");
|
||||
}
|
||||
|
||||
private void ProcessPropertyAttributes(PropertyInfo property)
|
||||
{
|
||||
var attributes = property.GetCustomAttributes(typeof(SectionPropertyAttribute), true);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
Debug.Assert(attributes.Length == 1, "Too many section property attributes");
|
||||
this.Properties.Add(property.Name, property);
|
||||
this.ProcessSectionPropertyAttribute(property.PropertyType, property.Name, attributes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessSectionPropertyAttribute(System.Type? type, string name, object attribute)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(type, nameof(type));
|
||||
this.ProcessSectionPropertyAttribute(type, name, (SectionPropertyAttribute)attribute);
|
||||
}
|
||||
|
||||
public void AddStringKey(string key)
|
||||
{
|
||||
var added = this.StringKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddEnumerationKey(string key)
|
||||
{
|
||||
var added = this.EnumerationKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddMultiplesKey(string key)
|
||||
{
|
||||
var added = this.MultipleKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddHexIntegerKey(string key)
|
||||
{
|
||||
var added = this.HexIntegerKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddIntegerKey(string key)
|
||||
{
|
||||
var added = this.IntegerKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddHexLongKey(string key)
|
||||
{
|
||||
var added = this.HexLongKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddLongKey(string key)
|
||||
{
|
||||
var added = this.LongKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
public void AddDateTimeKey(string key)
|
||||
{
|
||||
var added = this.DateTimeKeys.Add(key);
|
||||
Debug.Assert(added, $"<{key}> already has an entry");
|
||||
}
|
||||
|
||||
private void ProcessSectionPropertyAttribute(System.Type originalType, string name, SectionPropertyAttribute attribute)
|
||||
{
|
||||
var key = attribute.Key;
|
||||
this._entriesToProperties.Add(key, name);
|
||||
|
||||
if (attribute is SectionReferenceAttribute)
|
||||
{
|
||||
this.ReferenceAttributes.Add(key, new Tuple<string, System.Type>(name, originalType));
|
||||
} else if (attribute is SectionReferencesAttribute referencesAttribute)
|
||||
{
|
||||
this.ReferencesAttributes.Add(key, new Tuple<string, System.Type>(name, originalType));
|
||||
}
|
||||
|
||||
var multiples = attribute.Many;
|
||||
if (multiples)
|
||||
{
|
||||
// Type is irrelevant
|
||||
this.AddMultiplesKey(key);
|
||||
return;
|
||||
}
|
||||
|
||||
var type = attribute.Type ?? originalType;
|
||||
|
||||
var enumeration = attribute.Enumeration;
|
||||
if (enumeration)
|
||||
{
|
||||
Debug.Assert(type == typeof(string), "Enumeration must be of type string");
|
||||
this.AddEnumerationKey(key);
|
||||
return;
|
||||
}
|
||||
|
||||
var hex = attribute.Hexadecimal;
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
Debug.Assert(!enumeration, "Enumeration case should already have been handled");
|
||||
Debug.Assert(!hex, "Cannot have a hexadecimal string type");
|
||||
this.AddStringKey(key);
|
||||
}
|
||||
else if (type == typeof(int) || type == typeof(Nullable<int>))
|
||||
{
|
||||
if (hex)
|
||||
{
|
||||
this.AddHexIntegerKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.AddIntegerKey(key);
|
||||
}
|
||||
}
|
||||
else if (type == typeof(long) || type == typeof(Nullable<long>))
|
||||
{
|
||||
if (hex)
|
||||
{
|
||||
this.AddHexLongKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.AddLongKey(key);
|
||||
}
|
||||
}
|
||||
else if (type == typeof(DateTime) || type == typeof(Nullable<DateTime>))
|
||||
{
|
||||
this.AddDateTimeKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Property type <{type}> has not been implemented");
|
||||
}
|
||||
this.AddLongKey(key);
|
||||
}
|
||||
}
|
||||
else if (type == typeof(DateTime) || type == typeof(Nullable<DateTime>))
|
||||
{
|
||||
this.AddDateTimeKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Property type <{type}> has not been implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
//scope id = 0, name = "", mod = 0, size = 1137, span = 355 + 354
|
||||
//scope id = 1, name = "stack", mod = 0, type = scope, size = 7, parent = 0, span = 15
|
||||
//scope id = 7, name = "print_box_break_vertical", mod = 0, type = scope, size = 6, parent = 0, sym = 33, span = 72
|
||||
[Section("scope", "Scopes")]
|
||||
public sealed class Scope(Parser container) : NamedSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
//scope id = 0, name = "", mod = 0, size = 1137, span = 355 + 354
|
||||
//scope id = 1, name = "stack", mod = 0, type = scope, size = 7, parent = 0, span = 15
|
||||
//scope id = 7, name = "print_box_break_vertical", mod = 0, type = scope, size = 6, parent = 0, sym = 33, span = 72
|
||||
[Section("scope", "Scopes")]
|
||||
public sealed class Scope(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionReference("mod")]
|
||||
public Symbols.Module? Module { get; private set; }
|
||||
[SectionReference("mod")]
|
||||
public Symbols.Module? Module { get; private set; }
|
||||
|
||||
[SectionProperty("type")]
|
||||
public string? Type { get; private set; }
|
||||
[SectionProperty("type")]
|
||||
public string? Type { get; private set; }
|
||||
|
||||
[SectionProperty("size")]
|
||||
public int Size { get; private set; }
|
||||
[SectionProperty("size")]
|
||||
public int Size { get; private set; }
|
||||
|
||||
[SectionReference("parent", optional: true)]
|
||||
public Scope? Parent { get; private set; }
|
||||
[SectionReference("parent", optional: true)]
|
||||
public Scope? Parent { get; private set; }
|
||||
|
||||
[SectionReference("sym", optional: true)]
|
||||
public Symbols.Symbol? Symbol { get; private set; }
|
||||
[SectionReference("sym", optional: true)]
|
||||
public Symbols.Symbol? Symbol { get; private set; }
|
||||
|
||||
[SectionReferences("span")]
|
||||
public List<Span>? Spans { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionReferences("span")]
|
||||
public List<Span>? Spans { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,162 +1,156 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
public class Section
|
||||
{
|
||||
namespace Symbols
|
||||
protected static readonly Dictionary<System.Type, ReflectedSectionProperties> _sectionPropertiesCache = [];
|
||||
protected static readonly DateTime _unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
protected readonly Parser _container;
|
||||
|
||||
// Needed to evaluate references on a second pass
|
||||
protected readonly Dictionary<string, int> _references = [];
|
||||
protected readonly Dictionary<string, List<int>> _multipleReferences = [];
|
||||
|
||||
protected Dictionary<string, string>? _parsed;
|
||||
|
||||
protected ReflectedSectionProperties SectionProperties => GetSectionProperties(this.GetType());
|
||||
|
||||
protected static ReflectedSectionProperties GetSectionProperties(System.Type type)
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
var obtained = _sectionPropertiesCache.TryGetValue(type, out var properties);
|
||||
Debug.Assert(obtained, $"Section properties for {type.Name} have not been built");
|
||||
Debug.Assert(properties != null);
|
||||
return properties;
|
||||
}
|
||||
|
||||
public class Section
|
||||
protected Section(Parser container) => this._container = container;
|
||||
|
||||
protected void ProcessAttributesOfProperties()
|
||||
{
|
||||
var type = this.GetType();
|
||||
var entry = new ReflectedSectionProperties();
|
||||
Debug.Assert(_sectionPropertiesCache != null);
|
||||
if (_sectionPropertiesCache.TryAdd(type, entry))
|
||||
{
|
||||
protected static readonly Dictionary<System.Type, ReflectedSectionProperties> _sectionPropertiesCache = [];
|
||||
protected static readonly DateTime _unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
protected readonly Parser _container;
|
||||
|
||||
// Needed to evaluate references on a second pass
|
||||
protected readonly Dictionary<string, int> _references = [];
|
||||
protected readonly Dictionary<string, List<int>> _multipleReferences = [];
|
||||
|
||||
protected Dictionary<string, string>? _parsed;
|
||||
|
||||
protected ReflectedSectionProperties SectionProperties => GetSectionProperties(this.GetType());
|
||||
|
||||
protected static ReflectedSectionProperties GetSectionProperties(System.Type type)
|
||||
{
|
||||
var obtained = _sectionPropertiesCache.TryGetValue(type, out var properties);
|
||||
Debug.Assert(obtained, $"Section properties for {type.Name} have not been built");
|
||||
Debug.Assert(properties != null);
|
||||
return properties;
|
||||
}
|
||||
|
||||
protected Section(Parser container) => this._container = container;
|
||||
|
||||
protected void ProcessAttributesOfProperties()
|
||||
{
|
||||
var type = this.GetType();
|
||||
var entry = new ReflectedSectionProperties();
|
||||
Debug.Assert(_sectionPropertiesCache != null);
|
||||
if (_sectionPropertiesCache.TryAdd(type, entry))
|
||||
{
|
||||
entry.Build(type);
|
||||
}
|
||||
}
|
||||
|
||||
public T GetValueT<T>(string key) => this.SectionProperties.GetValueT<T>(this, key);
|
||||
|
||||
public virtual void Parse(Dictionary<string, string> entries)
|
||||
{
|
||||
this._parsed = entries;
|
||||
this._container.SectionEntries.Add(this);
|
||||
this.ProcessAttributesOfProperties();
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var (key, value) = entry;
|
||||
this.Parse(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void Parse(string key, string value)
|
||||
{
|
||||
var propertyName = this.SectionProperties.GetPropertyNameFromEntryName(key);
|
||||
var propertyAvailable = this.SectionProperties.Properties.TryGetValue(propertyName, out var property);
|
||||
if (!propertyAvailable)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to locate property name {propertyName} using reflection");
|
||||
}
|
||||
Debug.Assert(property != null);
|
||||
this.Parse(property, key, value);
|
||||
}
|
||||
|
||||
private void Parse(PropertyInfo property, string key, string value)
|
||||
{
|
||||
var reference = this.SectionProperties.ReferenceAttributes.ContainsKey(key);
|
||||
var references = this.SectionProperties.ReferencesAttributes.ContainsKey(key);
|
||||
var lazy = reference || references;
|
||||
|
||||
if (lazy)
|
||||
{
|
||||
if (reference)
|
||||
{
|
||||
this._references.Add(key, ExtractInteger(value));
|
||||
}
|
||||
else if (references)
|
||||
{
|
||||
this._multipleReferences.Add(key, ExtractCompoundInteger(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Getting here should be impossible! Key {key} is lazy, but not a reference");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.SectionProperties.StringKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractString(value));
|
||||
}
|
||||
else if (this.SectionProperties.EnumerationKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractEnumeration(value));
|
||||
}
|
||||
else if (this.SectionProperties.IntegerKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractInteger(value));
|
||||
}
|
||||
else if (this.SectionProperties.HexIntegerKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractHexInteger(value));
|
||||
}
|
||||
else if (this.SectionProperties.LongKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractLong(value));
|
||||
}
|
||||
else if (this.SectionProperties.HexLongKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractHexLong(value));
|
||||
}
|
||||
else if (this.SectionProperties.DateTimeKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractDateTime(value));
|
||||
}
|
||||
else if (this.SectionProperties.MultipleKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractCompoundInteger(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Section: {key} has not been categorised");
|
||||
}
|
||||
}
|
||||
|
||||
protected static string ExtractString(string value) => value.Trim('"');
|
||||
protected static string ExtractEnumeration(string value) => value;
|
||||
|
||||
private static T ExtractHexValue<T>(string value) where T : INumberBase<T> => T.Parse(value.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
private static T ExtractNumericValue<T>(string value) where T : IParsable<T> => T.Parse(value, CultureInfo.InvariantCulture);
|
||||
|
||||
protected static int ExtractHexInteger(string value) => ExtractHexValue<int>(value);
|
||||
protected static long ExtractHexLong(string value) => ExtractHexValue<long>(value);
|
||||
protected static int ExtractInteger(string value) => ExtractNumericValue<int>(value);
|
||||
protected static long ExtractLong(string value) => ExtractNumericValue<long>(value);
|
||||
protected static DateTime ExtractDateTime(string value) => _unixEpoch.AddSeconds(ExtractHexLong(value));
|
||||
protected static string[] ExtractCompoundString(string value) => value.Split('+');
|
||||
|
||||
protected static List<int> ExtractCompoundInteger(string value)
|
||||
{
|
||||
var elements = ExtractCompoundString(value);
|
||||
var returned = new List<int>(elements.Length);
|
||||
foreach (var element in elements)
|
||||
{
|
||||
returned.Add(ExtractInteger(element));
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
entry.Build(type);
|
||||
}
|
||||
}
|
||||
|
||||
public T GetValueT<T>(string key) => this.SectionProperties.GetValueT<T>(this, key);
|
||||
|
||||
public virtual void Parse(Dictionary<string, string> entries)
|
||||
{
|
||||
this._parsed = entries;
|
||||
this._container.SectionEntries.Add(this);
|
||||
this.ProcessAttributesOfProperties();
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var (key, value) = entry;
|
||||
this.Parse(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void Parse(string key, string value)
|
||||
{
|
||||
var propertyName = this.SectionProperties.GetPropertyNameFromEntryName(key);
|
||||
var propertyAvailable = this.SectionProperties.Properties.TryGetValue(propertyName, out var property);
|
||||
if (!propertyAvailable)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to locate property name {propertyName} using reflection");
|
||||
}
|
||||
Debug.Assert(property != null);
|
||||
this.Parse(property, key, value);
|
||||
}
|
||||
|
||||
private void Parse(PropertyInfo property, string key, string value)
|
||||
{
|
||||
var reference = this.SectionProperties.ReferenceAttributes.ContainsKey(key);
|
||||
var references = this.SectionProperties.ReferencesAttributes.ContainsKey(key);
|
||||
var lazy = reference || references;
|
||||
|
||||
if (lazy)
|
||||
{
|
||||
if (reference)
|
||||
{
|
||||
this._references.Add(key, ExtractInteger(value));
|
||||
}
|
||||
else if (references)
|
||||
{
|
||||
this._multipleReferences.Add(key, ExtractCompoundInteger(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Getting here should be impossible! Key {key} is lazy, but not a reference");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.SectionProperties.StringKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractString(value));
|
||||
}
|
||||
else if (this.SectionProperties.EnumerationKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractEnumeration(value));
|
||||
}
|
||||
else if (this.SectionProperties.IntegerKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractInteger(value));
|
||||
}
|
||||
else if (this.SectionProperties.HexIntegerKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractHexInteger(value));
|
||||
}
|
||||
else if (this.SectionProperties.LongKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractLong(value));
|
||||
}
|
||||
else if (this.SectionProperties.HexLongKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractHexLong(value));
|
||||
}
|
||||
else if (this.SectionProperties.DateTimeKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractDateTime(value));
|
||||
}
|
||||
else if (this.SectionProperties.MultipleKeys.Contains(key))
|
||||
{
|
||||
property?.SetValue(this, ExtractCompoundInteger(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Section: {key} has not been categorised");
|
||||
}
|
||||
}
|
||||
|
||||
protected static string ExtractString(string value) => value.Trim('"');
|
||||
protected static string ExtractEnumeration(string value) => value;
|
||||
|
||||
private static T ExtractHexValue<T>(string value) where T : INumberBase<T> => T.Parse(value.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
private static T ExtractNumericValue<T>(string value) where T : IParsable<T> => T.Parse(value, CultureInfo.InvariantCulture);
|
||||
|
||||
protected static int ExtractHexInteger(string value) => ExtractHexValue<int>(value);
|
||||
protected static long ExtractHexLong(string value) => ExtractHexValue<long>(value);
|
||||
protected static int ExtractInteger(string value) => ExtractNumericValue<int>(value);
|
||||
protected static long ExtractLong(string value) => ExtractNumericValue<long>(value);
|
||||
protected static DateTime ExtractDateTime(string value) => _unixEpoch.AddSeconds(ExtractHexLong(value));
|
||||
protected static string[] ExtractCompoundString(string value) => value.Split('+');
|
||||
|
||||
protected static List<int> ExtractCompoundInteger(string value)
|
||||
{
|
||||
var elements = ExtractCompoundString(value);
|
||||
var returned = new List<int>(elements.Length);
|
||||
foreach (var element in elements)
|
||||
{
|
||||
returned.Add(ExtractInteger(element));
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal class SectionAttribute(string key, string? referencing = null) : Attribute
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal class SectionAttribute(string key, string? referencing = null) : Attribute
|
||||
{
|
||||
public string Key { get; private set; } = key;
|
||||
public string Key { get; private set; } = key;
|
||||
|
||||
public string? Referencing { get; private set; } = referencing;
|
||||
}
|
||||
}
|
||||
public string? Referencing { get; private set; } = referencing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionEnumerationAttribute(string key) : SectionPropertyAttribute(key, enumeration: true)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionEnumerationAttribute(string key) : SectionPropertyAttribute(key, enumeration: true)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal class SectionPropertyAttribute(string key, System.Type? type = null, bool enumeration = false, bool hexadecimal = false, bool optional = false, bool many = false) : Attribute
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal class SectionPropertyAttribute(string key, System.Type? type = null, bool enumeration = false, bool hexadecimal = false, bool optional = false, bool many = false) : Attribute
|
||||
{
|
||||
public string Key { get; private set; } = key;
|
||||
public string Key { get; private set; } = key;
|
||||
|
||||
public System.Type? Type { get; private set; } = type;
|
||||
public System.Type? Type { get; private set; } = type;
|
||||
|
||||
public bool Enumeration { get; private set; } = enumeration;
|
||||
public bool Enumeration { get; private set; } = enumeration;
|
||||
|
||||
public bool Hexadecimal { get; private set; } = hexadecimal;
|
||||
public bool Hexadecimal { get; private set; } = hexadecimal;
|
||||
|
||||
public bool Optional { get; private set; } = optional;
|
||||
public bool Optional { get; private set; } = optional;
|
||||
|
||||
public bool Many { get; private set; } = many;
|
||||
}
|
||||
}
|
||||
public bool Many { get; private set; } = many;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionReferenceAttribute(string key, bool optional = false) : SectionPropertyAttribute(key, type: typeof(int), optional: optional)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionReferenceAttribute(string key, bool optional = false) : SectionPropertyAttribute(key, type: typeof(int), optional: optional)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionReferencesAttribute(string key) : SectionPropertyAttribute(key, many: true)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class SectionReferencesAttribute(string key) : SectionPropertyAttribute(key, many: true)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
// seg id=1,name="RODATA",start=0x00F471,size=0x0000,addrsize=absolute,type=ro,oname="sudoku.65b",ooffs=1137
|
||||
[Section("seg", "Segments")]
|
||||
public sealed class Segment(Parser container) : NamedSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
// seg id=1,name="RODATA",start=0x00F471,size=0x0000,addrsize=absolute,type=ro,oname="sudoku.65b",ooffs=1137
|
||||
[Section("seg", "Segments")]
|
||||
public sealed class Segment(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionProperty("start", hexadecimal: true)]
|
||||
public int Start { get; private set; }
|
||||
[SectionProperty("start", hexadecimal: true)]
|
||||
public int Start { get; private set; }
|
||||
|
||||
[SectionProperty("size", hexadecimal: true)]
|
||||
public int Size { get; private set; }
|
||||
[SectionProperty("size", hexadecimal: true)]
|
||||
public int Size { get; private set; }
|
||||
|
||||
[SectionEnumeration("addrsize")]
|
||||
public string? AddressSize { get; private set; }
|
||||
[SectionEnumeration("addrsize")]
|
||||
public string? AddressSize { get; private set; }
|
||||
|
||||
[SectionEnumeration("type")]
|
||||
public string? Type { get; private set; }
|
||||
[SectionEnumeration("type")]
|
||||
public string? Type { get; private set; }
|
||||
|
||||
[SectionProperty("oname")]
|
||||
public string? OName { get; private set; }
|
||||
[SectionProperty("oname")]
|
||||
public string? OName { get; private set; }
|
||||
|
||||
[SectionProperty("ooffs")]
|
||||
public int? OOFFS { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionProperty("ooffs")]
|
||||
public int? OOFFS { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,19 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
//span id = 351, seg = 7, start = 0, size = 2, type = 2
|
||||
[Section("span", "Spans")]
|
||||
public sealed class Span(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
//span id = 351, seg = 7, start = 0, size = 2, type = 2
|
||||
[Section("span", "Spans")]
|
||||
public sealed class Span(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
[SectionReference("seg")]
|
||||
public Symbols.Segment? Segment { get; private set; }
|
||||
[SectionReference("seg")]
|
||||
public Symbols.Segment? Segment { get; private set; }
|
||||
|
||||
[SectionProperty("start")]
|
||||
public int Start { get; private set; }
|
||||
[SectionProperty("start")]
|
||||
public int Start { get; private set; }
|
||||
|
||||
[SectionProperty("size")]
|
||||
public int Size { get; private set; }
|
||||
[SectionProperty("size")]
|
||||
public int Size { get; private set; }
|
||||
|
||||
[SectionReference("type")]
|
||||
public Symbols.Type? Type { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionReference("type")]
|
||||
public Symbols.Type? Type { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,33 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
using System.Collections.Generic;
|
||||
|
||||
// sym id = 16, name = "solve", addrsize = absolute, size = 274, scope = 0, def = 94,ref=144+17+351,val=0xF314,seg=6,type=lab
|
||||
[Section("symbol", "Symbols")]
|
||||
public sealed class Symbol(Parser container) : NamedSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
[SectionEnumeration("addrsize")]
|
||||
public string? AddressSize { get; private set; }
|
||||
|
||||
// sym id = 16, name = "solve", addrsize = absolute, size = 274, scope = 0, def = 94,ref=144+17+351,val=0xF314,seg=6,type=lab
|
||||
[Section("symbol", "Symbols")]
|
||||
public sealed class Symbol(Parser container) : NamedSection(container)
|
||||
{
|
||||
[SectionEnumeration("addrsize")]
|
||||
public string? AddressSize { get; private set; }
|
||||
[SectionProperty("size")]
|
||||
public int? Size { get; private set; }
|
||||
|
||||
[SectionProperty("size")]
|
||||
public int? Size { get; private set; }
|
||||
[SectionReference("scope")]
|
||||
public Symbols.Scope? Scope { get; private set; }
|
||||
|
||||
[SectionReference("scope")]
|
||||
public Symbols.Scope? Scope { get; private set; }
|
||||
[SectionReferences("def")]
|
||||
public List<Line>? Definitions { get; private set; }
|
||||
|
||||
[SectionReferences("def")]
|
||||
public List<Line>? Definitions { get; private set; }
|
||||
[SectionReferences("ref")]
|
||||
public List<Line>? References { get; private set; }
|
||||
|
||||
[SectionReferences("ref")]
|
||||
public List<Line>? References { get; private set; }
|
||||
[SectionProperty("val", hexadecimal: true)]
|
||||
public int Value { get; private set; }
|
||||
|
||||
[SectionProperty("val", hexadecimal: true)]
|
||||
public int Value { get; private set; }
|
||||
[SectionReference("seg")]
|
||||
public Symbols.Segment? Segment { get; private set; }
|
||||
|
||||
[SectionReference("seg")]
|
||||
public Symbols.Segment? Segment { get; private set; }
|
||||
|
||||
[SectionEnumeration("type")]
|
||||
public string? Type { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionEnumeration("type")]
|
||||
public string? Type { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
// type id = 0, val = "800920"
|
||||
[Section("type", "Types")]
|
||||
public sealed class Type(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
// type id = 0, val = "800920"
|
||||
[Section("type", "Types")]
|
||||
public sealed class Type(Parser container) : IdentifiableSection(container)
|
||||
{
|
||||
[SectionProperty("val")]
|
||||
public string? Value { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionProperty("val")]
|
||||
public string? Value { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,13 @@
|
||||
namespace EightBit
|
||||
namespace EightBit.Files.Symbols
|
||||
{
|
||||
namespace Files
|
||||
//version major = 2, minor = 0
|
||||
[Section("version")]
|
||||
public sealed class Version(Parser container) : Section(container)
|
||||
{
|
||||
namespace Symbols
|
||||
{
|
||||
//version major = 2, minor = 0
|
||||
[Section("version")]
|
||||
public sealed class Version(Parser container) : Section(container)
|
||||
{
|
||||
[SectionProperty("major")]
|
||||
public int Major { get; private set; }
|
||||
[SectionProperty("major")]
|
||||
public int Major { get; private set; }
|
||||
|
||||
[SectionProperty("minor")]
|
||||
public int Minor { get; private set; }
|
||||
}
|
||||
}
|
||||
[SectionProperty("minor")]
|
||||
public int Minor { get; private set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user