///|
/// Identifies a half-open source-character range.
pub(all) struct SourceSpan {
  start : Int
  end : Int
} derive(Eq, Debug)

///|
/// Classifies one parsed Roman token.
pub(all) enum RomanTokenRole {
  SymbolToken
  SubtractiveToken
  GroupOpenToken
  GroupCloseToken
} derive(Eq, Debug)

///|
/// Retains source and normalized evidence for one token.
pub(all) struct RomanToken {
  span : SourceSpan
  source_text : String
  normalized_text : String
  value : Int
  role : RomanTokenRole
} derive(Eq, Debug)

///|
/// Identifies a machine-readable diagnostic category.
pub(all) enum DiagnosticCode {
  EmptyInputCode
  InvalidConfigurationCode
  UppercaseRequiredCode
  LowercaseRequiredCode
  UnicodeSourceNotAllowedCode
  OuterWhitespaceNotAllowedCode
  SingleSymbolExcludedCode
  CandidateTooLongCode
  UnsupportedCharacterCode
  LowercaseNotAllowedCode
  InvalidRepetitionCode
  InvalidSubtractionCode
  InvalidOrderCode
  MalformedParenthesesCode
  NestedParenthesesCode
  EmptyParenthesizedGroupCode
  NonCanonicalCode
  ValueOutOfRangeCode
} derive(Eq, Debug)

///|
/// Distinguishes fatal errors from retained normalization information.
pub(all) enum DiagnosticSeverity {
  DiagnosticError
  DiagnosticWarning
} derive(Eq, Debug)

///|
/// Describes one source-located processing observation.
pub(all) struct RomanDiagnostic {
  code : DiagnosticCode
  severity : DiagnosticSeverity
  message : String
  span : SourceSpan
  replacement : String?
} derive(Eq, Debug)

///|
/// Returns complete, reproducible evidence for one accepted numeral.
pub(all) struct ParseReport {
  original : String
  normalized : String
  value : Int
  canonical : String
  tokens : Array[RomanToken]
  diagnostics : Array[RomanDiagnostic]
  notices : Array[String]
  used_unicode_compatibility : Bool
  trimmed_outer_whitespace : Bool
} derive(Eq, Debug)

///|
/// Reports configured parsing failures without requiring string parsing.
pub(all) enum RomanReportError {
  EmptyRomanInput
  UnsupportedRomanCharacter(SourceSpan, Char)
  LowercaseRomanNotAllowed(SourceSpan, Char)
  InvalidRomanGrammar(SourceSpan, DiagnosticCode)
  NonCanonicalRoman(String)
  RomanReportOutOfRange(Int)
  InvalidRomanConfiguration(ConfigError)
} derive(Eq, Debug)