/* * Copyright 2019 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Diagnostics; namespace SourceGen { /// /// Symbolic representation of a value. Instances are immutable. /// public class Symbol { /// /// Was the symbol defined by the user, or generated automatically? /// public enum Source { // These are in order of highest to lowest precedence. This matters when // looking up a symbol by value, since multiple symbols can have the same value. Unknown = 0, User, // user-defined label Project, // from project configuration file Platform, // from platform definition file Auto // auto-generated label } /// /// Local internal label, global internal label, or reference to an /// external address? Constants get a separate type in case we need to /// distinguish them from addresses. /// public enum Type { Unknown = 0, LocalOrGlobalAddr, // local symbol, may be promoted to global GlobalAddr, // user wants this to be a global symbol GlobalAddrExport, // global symbol that is exported to linkers ExternalAddr, // reference to address outside program (e.g. platform sym file) Constant // constant value } /// Returns true if the symbol's type is an internal label (auto or user). Returns /// false for external addresses and constants. /// public bool IsInternalLabel { get { // Could also check Type instead. Either works for now. return SymbolSource == Source.User || SymbolSource == Source.Auto; } } /// /// Label sent to assembler. /// public string Label { get; private set; } /// /// Symbol's numeric value. /// public int Value { get; private set; } /// /// Symbol origin, e.g. auto-generated or entered by user. /// public Source SymbolSource { get; private set; } /// /// Type of symbol, e.g. local or global. /// public Type SymbolType { get; private set; } /// /// Two-character string representation of Source and Type, for display in the UI. /// public string SourceTypeString { get; private set; } // No nullary constructor. private Symbol() { } /// /// Constructs immutable object. /// /// Label string. Syntax assumed valid. /// User-defined or auto-generated? /// Type of symbol this is. /// user-defined. public Symbol(string label, int value, Source source, Type type) { Debug.Assert(!string.IsNullOrEmpty(label)); Label = label; Value = value; SymbolType = type; SymbolSource = source; // Generate SourceTypeString. string sts; switch (SymbolSource) { case Source.Auto: sts = "A"; break; case Source.User: sts = "U"; break; case Source.Platform: sts = "P"; break; case Source.Project: sts = "R"; break; default: sts = "?"; break; } switch (SymbolType) { case Type.LocalOrGlobalAddr: sts += "L"; break; case Type.GlobalAddr: sts += "G"; break; case Type.GlobalAddrExport: sts += "X"; break; case Type.ExternalAddr: sts += "E"; break; case Type.Constant: sts += "C"; break; default: sts += "?"; break; } SourceTypeString = sts; } public override string ToString() { return Label + "{" + SymbolSource + "," + SymbolType + ",val=$" + Value.ToString("x4") + "}"; } public static bool operator ==(Symbol a, Symbol b) { if (ReferenceEquals(a, b)) { return true; // same object, or both null } if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) { return false; // one is null } // All fields must be equal. Ignore SourceTypeString, since it's generated // from Source and Type. return Asm65.Label.LABEL_COMPARER.Equals(a.Label, b.Label) && a.Value == b.Value && a.SymbolSource == b.SymbolSource && a.SymbolType == b.SymbolType; } public static bool operator !=(Symbol a, Symbol b) { return !(a == b); } public override bool Equals(object obj) { return obj is Symbol && this == (Symbol)obj; } public override int GetHashCode() { // Convert the label to upper case before computing the hash code, so that // symbols with "foo" and "FOO" (which are equal) have the same hash code. return Asm65.Label.ToNormal(Label).GetHashCode() ^ Value ^ (int)SymbolType ^ (int)SymbolSource; } // // Comparison function, used when sorting the symbol table. // public enum SymbolSortField { CombinedType, Value, Name }; public static int Compare(SymbolSortField sortField, bool isAscending, Symbol a, Symbol b) { // In the symbol table, only the label field is guaranteed to be unique. We // use it as a secondary sort key when comparing the other fields. switch (sortField) { case SymbolSortField.CombinedType: if (isAscending) { int cmp = string.Compare(a.SourceTypeString, b.SourceTypeString); if (cmp == 0) { cmp = string.Compare(a.Label, b.Label); } return cmp; } else { int cmp = string.Compare(a.SourceTypeString, b.SourceTypeString); if (cmp == 0) { // secondary sort is always ascending, so negate cmp = -string.Compare(a.Label, b.Label); } return -cmp; } case SymbolSortField.Value: if (isAscending) { int cmp; if (a.Value < b.Value) { cmp = -1; } else if (a.Value > b.Value) { cmp = 1; } else { cmp = string.Compare(a.Label, b.Label); } return cmp; } else { int cmp; if (a.Value < b.Value) { cmp = -1; } else if (a.Value > b.Value) { cmp = 1; } else { cmp = -string.Compare(a.Label, b.Label); } return -cmp; } case SymbolSortField.Name: if (isAscending) { return string.Compare(a.Label, b.Label); } else { return -string.Compare(a.Label, b.Label); } default: Debug.Assert(false); return 0; } } } }