Roman numeral parsing, linting, safe rewriting, and document indexing for MoonBit
moon add Armyhealthyhappy/moonbit-romanimport {
"Armyhealthyhappy/moonbit-roman" @roman,
}let text = @roman.format(2026).unwrap() // MMXXVI
let value = @roman.parse("mcmxciv").unwrap() // 1994
let upper = @roman.canonicalize("mmxxvi").unwrap()
let clock = @roman.format_clock_hour(4).unwrap() // IIIIlet extended = @roman.format_with_config(
944944,
@roman.extended_format_config(),
).unwrap() // (CMXLIV)CMXLIV
let report = @roman.parse_with_config(
"(IV)",
@roman.extended_parse_config(),
).unwrap()
println(report.value) // 4000
println(report.canonical) // (IV)| Profile | Accepted/formatted range | Deterministic form |
|---|---|---|
| ModernCanonical | 1..3999 | Conventional modern subtractive form |
| AdditiveHistorical | 1..3999 | Additive place form such as VIIII |
| ClockFace | 1..12 | Clock-hour form using IIII for four |
| ParenthesizedThousands | 1..3,999,999 | One canonical group multiplied by 1000 plus a remainder below 1000 |
let report = @roman.parse_with_config(
" \u{2163} ",
@roman.compatibility_parse_config(),
).unwrap()
println(report.normalized) // IV
println(report.used_unicode_compatibility) // true
println(report.trimmed_outer_whitespace) // truelet config : @roman.ScanConfig = {
parse_config: @roman.compatibility_parse_config(),
retain_rejected: true,
include_single_symbol: false,
max_candidate_length: 32,
}
let scan = @roman.scan_roman_text(
"Release XIV includes \u{2163} checks and IIX.",
config,
).unwrap()let converted = @roman.convert_roman(
"IIII",
@roman.additive_parse_config(),
@roman.modern_format_config(),
).unwrap()
assert_eq(converted.output, "IV")moon run examples/basic
moon run examples/profiles
moon run examples/diagnostics
moon run examples/document_scan
moon run examples/lint_edits
moon run examples/index_querymoon fmt --check
moon info
moon check --deny-warn
moon build
moon test
pwsh -File scripts/source-audit.ps1pub(all) enum BatchFailureKind {
EmptyInputFailure
UnsupportedCharacterFailure
LowercaseNotAllowedFailure
GrammarFailure(DiagnosticCode)
NonCanonicalFailure
OutOfRangeFailure
InvalidConfigurationFailure
} derive(Eq, Debug)pub(all) enum ConversionError {
ConversionParseFailed(RomanReportError)
ConversionFormatFailed(FormatError)
} derive(Eq, Debug)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)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)pub(all) struct RewriteConfig {
scan_config : ScanConfig
target_format : FormatConfig
failure_policy : RewriteFailurePolicy
} derive(Eq, Debug)pub(all) struct RomanAnalysisReport {
original : String
config : ParseConfig
status : RomanAnalysisStatus
diagnostics : Array[RomanDiagnostic]
replacement : String?
observed_unicode_compatibility : Bool
observed_outer_whitespace : Bool
} derive(Eq, Debug)pub(all) enum RomanAnalysisStatus {
AnalysisAccepted(ParseReport)
AnalysisRejected(RomanReportError)
} derive(Eq, Debug)pub(all) struct RomanAppliedEdit {
id : String
source_span : SourceSpan
output_span : SourceSpan
source_text : String
replacement_text : String
numeric_value : Int?
reason : RomanEditReason
source_diagnostic_code : DiagnosticCode?
} derive(Eq, Debug)pub(all) enum RomanBatchOutcome {
BatchParsed(ParseReport)
BatchFailed(RomanReportError)
} derive(Eq, Debug)pub(all) struct RomanBatchReport {
results : Array[RomanBatchResult]
statistics : RomanBatchStatistics
} derive(Eq, Debug)pub(all) struct RomanBatchResult {
id : String
input : String
config : ParseConfig
outcome : RomanBatchOutcome
} derive(Eq, Debug)pub(all) struct RomanBatchStatistics {
total : Int
succeeded : Int
failed : Int
used_unicode_compatibility : Int
trimmed_outer_whitespace : Int
failure_counts : Array[BatchFailureCount]
} derive(Eq, Debug)pub(all) struct RomanConversionReport {
input : String
value : Int
output : String
source_report : ParseReport
target_config : FormatConfig
} derive(Eq, Debug)pub(all) struct RomanCorpusReport {
documents : Array[RomanDocumentResult]
statistics : RomanCorpusStatistics
} derive(Eq, Debug)pub(all) struct RomanDiagnostic {
code : DiagnosticCode
severity : DiagnosticSeverity
message : String
span : SourceSpan
replacement : String?
} derive(Eq, Debug)pub(all) struct RomanDocumentIndex {
entries : Array[RomanIndexEntry]
documents : Array[RomanIndexDocumentMetadata]
rejections : Array[RomanIndexRejectionSummary]
statistics : RomanIndexStatistics
} derive(Eq, Debug)pub(all) struct RomanDocumentLintCandidate {
span : SourceSpan
source_text : String
lint : RomanLintReport
} derive(Eq, Debug)pub(all) struct RomanDocumentLintReport {
original : String
policy : RomanLintPolicy
candidates : Array[RomanDocumentLintCandidate]
rejected_candidates : Array[RomanDocumentRejectedCandidate]
diagnostics : Array[RomanDiagnostic]
candidates_examined : Int
error_count : Int
warning_count : Int
conforms : Bool
} derive(Eq, Debug)pub(all) enum RomanDocumentOutcome {
CorpusScanned(RomanScanReport)
CorpusScanFailed(ConfigError)
} derive(Eq, Debug)pub(all) struct RomanDocumentRejectedCandidate {
span : SourceSpan
source_text : String
reason : ScanRejectionReason
diagnostic : RomanDiagnostic
} derive(Eq, Debug)pub(all) struct RomanDocumentResult {
id : String
text : String
outcome : RomanDocumentOutcome
} derive(Eq, Debug)pub(all) struct RomanEditApplicationReport {
original : String
output : String
applied_edits : Array[RomanAppliedEdit]
mapping_segments : Array[RomanEditMappingSegment]
statistics : RomanEditPlanStatistics
} derive(Eq, Debug)pub(all) enum RomanEditError {
InvalidRomanProjectedOutputLimit(Int64)
EmptyRomanEditId(Int)
DuplicateRomanEditId(String)
NegativeRomanEditSpan(String, SourceSpan)
ReversedRomanEditSpan(String, SourceSpan)
RomanEditSpanOutOfBounds(String, SourceSpan, Int)
OverlappingRomanEdits(String, String)
RomanEditSourceMismatch(String, String, String)
NoOpRomanEdit(String)
RomanProjectedOutputNotRepresentable(Int64, Int64)
RomanProjectedOutputLimitExceeded(Int64, Int64)
RomanEditPlanSourceMismatch(String, String)
RomanEditDocumentLintFailed(ConfigError)
RomanEditRewriteFailed(RomanRewriteError)
} derive(Eq, Debug)pub(all) struct RomanEditMappingSegment {
kind : RomanEditMappingKind
source_span : SourceSpan
output_span : SourceSpan
} derive(Eq, Debug)pub(all) struct RomanEditPlan {
original : String
config : RomanEditPlanConfig
edits : Array[RomanTextEdit]
statistics : RomanEditPlanStatistics
diagnostics : Array[RomanDiagnostic]
} derive(Eq, Debug)pub(all) struct RomanEditPreview {
original : String
applied_edits : Array[RomanAppliedEdit]
mapping_segments : Array[RomanEditMappingSegment]
statistics : RomanEditPlanStatistics
} derive(Eq, Debug)pub(all) enum RomanEditReason {
RomanCanonicalizationEdit
RomanCaseNormalizationEdit
RomanUnicodeExpansionEdit
RomanLintDiagnosticEdit(DiagnosticCode)
CallerRequestedEdit(String)
} derive(Eq, Debug)pub(all) struct RomanIndexCanonicalGroup {
canonical : String
entries : Array[RomanIndexEntry]
} derive(Eq, Debug)pub(all) enum RomanIndexConstructionError {
EmptyIndexDocumentId(Int)
DuplicateIndexDocumentId(String)
IndexDocumentScanFailed(String, ConfigError)
IndexReportDocumentCountMismatch(Int, Int)
IndexReportDocumentIdMismatch(Int, String, String)
IndexReportDocumentTextMismatch(Int, String, String)
IndexReportUnexpectedScanFailure(String, ConfigError)
IndexReportUnexpectedScanSuccess(String, ConfigError)
IndexReportScanFailureMismatch(String, ConfigError, ConfigError)
IndexScanReportMismatch(String)
IndexCorpusStatisticsMismatch(RomanCorpusStatistics, RomanCorpusStatistics)
IndexReportInvalidSpan(String, RomanIndexEvidenceKind, Int, SourceSpan, Int)
IndexReportSourceMismatch(String, RomanIndexEvidenceKind, Int, String, String)
IndexReportOutOfOrder(String, RomanIndexEvidenceKind, Int)
IndexReportOverlappingEvidence(String, RomanIndexEvidenceKind, Int, RomanIndexEvidenceKind, Int)
IndexReportCandidateCountTooSmall(String, Int, Int)
IndexReportMatchParseFailed(String, Int, RomanReportError)
IndexReportMatchEvidenceMismatch(String, Int)
IndexReportMatchPolicyMismatch(String, Int)
IndexReportUnexpectedRejectionDetails(String)
IndexReportRejectionReasonMismatch(String, Int)
} derive(Eq, Debug)pub(all) struct RomanIndexDocumentGroup {
document_id : String
entries : Array[RomanIndexEntry]
} derive(Eq, Debug)pub(all) struct RomanIndexDocumentSummary {
document_id : String
parse_mode : RomanMode
source_length : Int
candidates_examined : Int64
accepted : Int64
rejected : Int64
retained_rejections : Int64
first_ordinal : Int?
min_value : Int?
max_value : Int?
distinct_values : Int64
used_unicode : Int64
normalized_inputs : Int64
} derive(Eq, Debug)pub(all) struct RomanIndexEntry {
document_id : String
source_span : SourceSpan
source_text : String
value : Int
canonical : String
normalized_text : String
mode : RomanMode
used_unicode_compatibility : Bool
normalized_input : Bool
trimmed_outer_whitespace : Bool
token_count : Int
ordinal : Int
} derive(Eq, Debug)pub(all) struct RomanIndexModeGroup {
mode : RomanMode
entries : Array[RomanIndexEntry]
} derive(Eq, Debug)pub(all) struct RomanIndexNormalizationGroup {
normalized_input : Bool
entries : Array[RomanIndexEntry]
} derive(Eq, Debug)pub(all) struct RomanIndexQueryPage {
entries : Array[RomanIndexEntry]
total_matches : Int64
offset : Int
limit : Int?
} derive(Eq, Debug)pub(all) struct RomanIndexRejectionSummary {
document_id : String
source_span : SourceSpan
source_text : String
reason : ScanRejectionReason
} derive(Eq, Debug)pub(all) struct RomanIndexSummary {
document_count : Int64
entry_count : Int64
rejected_count : Int64
min_value : Int?
max_value : Int?
distinct_values : Int64
used_unicode : Int64
normalized_inputs : Int64
documents : Array[RomanIndexDocumentSummary]
} derive(Eq, Debug)pub(all) struct RomanIndexUnicodeGroup {
used_unicode : Bool
entries : Array[RomanIndexEntry]
} derive(Eq, Debug)pub(all) struct RomanIndexValueGroup {
value : Int
entries : Array[RomanIndexEntry]
} derive(Eq, Debug)pub(all) struct RomanLintPolicy {
parse_config : ParseConfig
case_requirement : RomanCaseRequirement
allow_unicode : Bool
allow_outer_whitespace : Bool
require_canonical : Bool
include_single_symbol : Bool
retain_rejected_candidates : Bool
max_candidate_length : Int
} derive(Eq, Debug)pub(all) struct RomanLintReport {
original : String
policy : RomanLintPolicy
analysis : RomanAnalysisReport
style_diagnostics : Array[RomanDiagnostic]
diagnostics : Array[RomanDiagnostic]
replacement : String?
error_count : Int
warning_count : Int
conforms : Bool
} derive(Eq, Debug)pub(all) struct RomanRangeConfig {
start : Int
end : Int
step : Int
format_config : FormatConfig
max_items : Int
} derive(Eq, Debug)pub(all) enum RomanRangeError {
ZeroRangeStep
RangeDirectionMismatch(Int, Int, Int)
InvalidRangeItemLimit(Int)
RangeItemLimitExceeded(Int, Int)
RangeValueFormatFailed(Int, FormatError)
} derive(Eq, Debug)pub(all) struct RomanReplacement {
source_span : SourceSpan
output_span : SourceSpan
source_text : String
replacement_text : String
value : Int
} derive(Eq, Debug)pub(all) enum RomanReportError {
EmptyRomanInput
UnsupportedRomanCharacter(SourceSpan, Char)
LowercaseRomanNotAllowed(SourceSpan, Char)
InvalidRomanGrammar(SourceSpan, DiagnosticCode)
NonCanonicalRoman(String)
RomanReportOutOfRange(Int)
InvalidRomanConfiguration(ConfigError)
} derive(Eq, Debug)pub(all) enum RomanRewriteError {
RewriteScanConfigurationFailed(ConfigError)
RewriteFormatFailed(SourceSpan, FormatError)
} derive(Eq, Debug)pub(all) struct RomanRewriteReport {
original : String
output : String
replacements : Array[RomanReplacement]
skipped : Array[RomanRewriteSkipped]
scan_report : RomanScanReport
} derive(Eq, Debug)pub(all) struct RomanRewriteSkipped {
source_span : SourceSpan
source_text : String
value : Int
error : FormatError
} derive(Eq, Debug)pub(all) struct RomanScanMatch {
span : SourceSpan
source_text : String
report : ParseReport
} derive(Eq, Debug)pub(all) struct RomanScanRejection {
span : SourceSpan
source_text : String
reason : ScanRejectionReason
} derive(Eq, Debug)pub(all) struct RomanScanReport {
matches : Array[RomanScanMatch]
rejections : Array[RomanScanRejection]
candidates_examined : Int
} derive(Eq, Debug)pub(all) struct RomanTextEdit {
id : String
span : SourceSpan
expected_source : String
replacement : String
numeric_value : Int?
reason : RomanEditReason
source_diagnostic_code : DiagnosticCode?
} derive(Eq, Debug)pub(all) struct RomanToken {
span : SourceSpan
source_text : String
normalized_text : String
value : Int
role : RomanTokenRole
} derive(Eq, Debug)pub(all) struct ScanConfig {
parse_config : ParseConfig
retain_rejected : Bool
include_single_symbol : Bool
max_candidate_length : Int
} derive(Eq, Debug)pub(all) enum ScanRejectionReason {
SingleSymbolExcluded
CandidateTooLong(Int)
CandidateParseFailed(RomanReportError)
} derive(Eq, Debug)fn apply_roman_edit_plan(source : String, plan : RomanEditPlan) -> Result[RomanEditApplicationReport, RomanEditError]fn build_roman_document_index(inputs : Array[RomanDocument]) -> Result[RomanDocumentIndex, RomanIndexConstructionError]fn build_roman_index_from_corpus(inputs : Array[RomanDocument], report : RomanCorpusReport) -> Result[RomanDocumentIndex, RomanIndexConstructionError]fn build_roman_index_from_scan(document_id : String, source : String, config : ScanConfig) -> Result[RomanDocumentIndex, RomanIndexConstructionError]fn build_roman_index_from_scan_report(document_id : String, source : String, config : ScanConfig, report : RomanScanReport) -> Result[RomanDocumentIndex, RomanIndexConstructionError]fn convert_roman(input : String, source_config : ParseConfig, target_config : FormatConfig) -> Result[RomanConversionReport, ConversionError]fn count_roman_index_matches(index : RomanDocumentIndex, query : RomanIndexQuery) -> Result[Int64, RomanIndexQueryError]fn find_roman_index_document(index : RomanDocumentIndex, document_id : String) -> RomanIndexDocumentMetadata?fn group_roman_index_entries_by_canonical(index : RomanDocumentIndex) -> Array[RomanIndexCanonicalGroup]fn group_roman_index_entries_by_document(index : RomanDocumentIndex) -> Array[RomanIndexDocumentGroup]fn group_roman_index_entries_by_normalization(index : RomanDocumentIndex) -> Array[RomanIndexNormalizationGroup]fn group_roman_index_entries_by_unicode(index : RomanDocumentIndex) -> Array[RomanIndexUnicodeGroup]fn is_canonical(input : String) -> Boolfn lint_roman_document(text : String, policy : RomanLintPolicy) -> Result[RomanDocumentLintReport, ConfigError]fn process_roman_batch(items : Array[RomanBatchItem]) -> Result[RomanBatchReport, BatchValidationError]fn process_roman_corpus(documents : Array[RomanDocument]) -> Result[RomanCorpusReport, CorpusValidationError]fn query_roman_index(index : RomanDocumentIndex, query : RomanIndexQuery) -> Result[Array[RomanIndexEntry], RomanIndexQueryError]fn query_roman_index_page(index : RomanDocumentIndex, query : RomanIndexQuery) -> Result[RomanIndexQueryPage, RomanIndexQueryError]fn rewrite_roman_text(text : String, config : RewriteConfig) -> Result[RomanRewriteReport, RomanRewriteError]fn roman_edit_plan_from_document_lint(report : RomanDocumentLintReport, config : RomanEditPlanConfig) -> Result[RomanEditPlan, RomanEditError]fn roman_edit_plan_from_rewrite(report : RomanRewriteReport, rewrite_config : RewriteConfig, edit_config : RomanEditPlanConfig) -> Result[RomanEditPlan, RomanEditError]fn roman_index_canonical_frequencies(index : RomanDocumentIndex) -> Array[RomanIndexCanonicalFrequency]fn roman_index_document_frequencies(index : RomanDocumentIndex) -> Array[RomanIndexDocumentFrequency]fn roman_index_entries_for_canonical_text(index : RomanDocumentIndex, canonical_text : String) -> Array[RomanIndexEntry]fn roman_index_entries_for_document(index : RomanDocumentIndex, document_id : String) -> Array[RomanIndexEntry]fn roman_index_entries_for_normalized_text(index : RomanDocumentIndex, normalized_text : String) -> Array[RomanIndexEntry]fn roman_index_entries_for_source_text(index : RomanDocumentIndex, source_text : String) -> Array[RomanIndexEntry]fn roman_index_entries_in_value_range(index : RomanDocumentIndex, minimum : Int, maximum : Int) -> Result[Array[RomanIndexEntry], RomanIndexQueryError]fn roman_index_normalized_frequencies(index : RomanDocumentIndex) -> Array[RomanIndexNormalizedFrequency]fn roman_index_rejections_for_document(index : RomanDocumentIndex, document_id : String) -> Array[RomanIndexRejectionSummary]fn roman_index_token_count_frequencies(index : RomanDocumentIndex) -> Array[RomanIndexTokenCountFrequency]fn validate_roman_edit_plan(source : String, edits : Array[RomanTextEdit], config : RomanEditPlanConfig) -> Result[RomanEditPlan, RomanEditError]Roman numeral parsing, linting, safe rewriting, and document indexing for MoonBit