Files
6502bench/Asm65/Label.cs
T
Andy McFadden 259593fa98 Increase max label length to 64
Labels were given an arbitrary maximum length of 32 characters, to
encourage backward compatibility with early assemblers and prevent
screen sprawl.  In practice, all supported cross-assemblers handle
extremely long variable names, and anybody who wants to port the
result back to a retro assembler will need to be aware of whatever
the specific limits are for it.

I prefer to have some limit, so I'm bumping the length from 32 to 64.
2026-03-31 10:48:33 -07:00

112 lines
4.7 KiB
C#

/*
* Copyright 2018 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.Text.RegularExpressions;
namespace Asm65 {
/// <summary>
/// Utility classes for working with labels.
///
/// The decision of whether to treat labels as case-sensitive or case-insensitive is
/// encapsulated here. All code should be case-preserving, but the comparison method
/// and "normal form" are defined here.
/// </summary>
public static class Label {
// Arbitrary choice for SourceGen. Different assemblers have different limits.
// The limit is currently hard-coded in the text for the EditLabel and EditDefSymbol
// dialogs, and is mentioned in the label definition in intro-details.html.
public const int MAX_LABEL_LEN = 64;
public const bool LABELS_CASE_SENSITIVE = true;
/// <summary>
/// String comparer to use when comparing labels.
/// </summary>
/// <remarks>
/// Usage:
/// if (Label.LABEL_COMPARER.Equals(label1, label2)) { ... }
///
/// We may want case-insensitive string compares, and we want the "invariant culture"
/// version for consistent results across users in multiple locales. (The labels are
/// expected to be ASCII strings, so the latter isn't crucial unless we change the
/// allowed set.)
/// </remarks>
public static readonly StringComparer LABEL_COMPARER = LABELS_CASE_SENSITIVE ?
StringComparer.InvariantCulture :
StringComparer.InvariantCultureIgnoreCase;
/// <summary>
/// Regex pattern for a valid label.
///
/// ASCII-only, starts with letter or underscore, followed by at least
/// one alphanumeric or underscore. Some assemblers may allow single-letter
/// labels, but I don't want to risk confusion with A/S/X/Y. So either we
/// reserve those, or we just mandate a two-character minimum.
/// </summary>
private static string sValidLabelPattern = @"^[a-zA-Z_][a-zA-Z0-9_]+$";
private static Regex sValidLabelCharRegex = new Regex(sValidLabelPattern);
/// <summary>
/// Validates a label, confirming that it is correctly formed.
/// </summary>
/// <param name="label">Label to validate.</param>
/// <returns>True if the label is correctly formed.</returns>
public static bool ValidateLabel(string label) {
if (label == null || label.Length > MAX_LABEL_LEN) {
return false;
}
MatchCollection matches = sValidLabelCharRegex.Matches(label);
return matches.Count == 1;
}
/// <summary>
/// Performs a detailed validation of a symbol label, breaking out different failure
/// causes for the benefit of code that reports errors to the user.
/// </summary>
/// <param name="label">Label to examine.</param>
/// <param name="isLenValid">True if the label has a valid length.</param>
/// <param name="isFirstCharValid">True if the first character is valid.</param>
/// <returns>True if the label is valid.</returns>
public static bool ValidateLabelDetail(string label, out bool isLenValid,
out bool isFirstCharValid) {
bool isValid = ValidateLabel(label);
if (isValid) {
isLenValid = isFirstCharValid = true;
return true;
}
// Something is wrong. Check length.
isLenValid = (label.Length >= 2 && label.Length <= MAX_LABEL_LEN);
// Check first char for alphanumeric or underscore.
isFirstCharValid = label.Length > 0 &&
((label[0] >= 'A' && label[0] <= 'Z') || (label[0] >= 'a' && label[0] <= 'z') ||
label[0] == '_');
return isValid;
}
/// <summary>
/// Returns "normal form" of label. This matches LABEL_COMPARER behavior.
/// </summary>
/// <param name="label">Label to transform.</param>
/// <returns>Transformed label.</returns>
public static string ToNormal(string label) {
return LABELS_CASE_SENSITIVE ? label : label.ToUpperInvariant();
}
}
}