///|
fn normalized_substring(text : String, start : Int, end : Int) -> String {
let chars = text.to_array()
let selected : Array[Char] = []
for index = start; index < end; index = index + 1 {
selected.push(chars[index])
}
String::from_array(selected)
}
///|
fn extended_structure_error(
input : NormalizedRomanInput,
index : Int,
code : DiagnosticCode,
) -> RomanReportError {
InvalidRomanGrammar(normalized_index_span(input, index, 1), code)
}
///|
fn find_extended_group_close(
input : NormalizedRomanInput,
) -> Result[Int, RomanReportError] {
let chars = input.normalized.to_array()
if chars.length() == 0 || chars[0] != '(' {
return Err(
InvalidRomanGrammar(input.content_span, MalformedParenthesesCode),
)
}
for index = 1; index < chars.length(); index = index + 1 {
if chars[index] == '(' {
return Err(extended_structure_error(input, index, NestedParenthesesCode))
}
if chars[index] == ')' {
if index == 1 {
return Err(
InvalidRomanGrammar(
normalized_index_span(input, 0, 2),
EmptyParenthesizedGroupCode,
),
)
}
return Ok(index)
}
}
Err(InvalidRomanGrammar(input.content_span, MalformedParenthesesCode))
}
///|
fn map_extended_segment_error(
input : NormalizedRomanInput,
error : ParseError,
offset : Int,
) -> RomanReportError {
match error {
EmptyInput => EmptyRomanInput
InvalidCharacter(index, character) =>
UnsupportedRomanCharacter(
normalized_index_span(input, offset + index, 1),
character,
)
InvalidRepetition(index, _) =>
InvalidRomanGrammar(
normalized_index_span(input, offset + index, 1),
InvalidRepetitionCode,
)
InvalidSubtractivePair(index, _, _) =>
InvalidRomanGrammar(
normalized_index_span(input, offset + index, 2),
InvalidSubtractionCode,
)
InvalidOrder(index) =>
InvalidRomanGrammar(
normalized_index_span(input, offset + index, 1),
InvalidOrderCode,
)
NonCanonical(expected) => NonCanonicalRoman(expected)
ParseOutOfRange(value) => RomanReportOutOfRange(value)
}
}
///|
fn parse_extended_segment(
input : NormalizedRomanInput,
start : Int,
end : Int,
) -> Result[Int, RomanReportError] {
let text = normalized_substring(input.normalized, start, end)
match parse(text) {
Ok(value) => Ok(value)
Err(error) => Err(map_extended_segment_error(input, error, start))
}
}
///|
fn reject_unexpected_parenthesis(
input : NormalizedRomanInput,
) -> RomanReportError? {
let chars = input.normalized.to_array()
for index = 0; index < chars.length(); index = index + 1 {
if chars[index] == '(' || chars[index] == ')' {
return Some(
extended_structure_error(input, index, MalformedParenthesesCode),
)
}
}
None
}
///|
fn parse_extended_candidate(
input : NormalizedRomanInput,
) -> Result[Int, RomanReportError] {
let chars = input.normalized.to_array()
if chars[0] != '(' {
match reject_unexpected_parenthesis(input) {
Some(error) => return Err(error)
None => ()
}
return match parse(input.normalized) {
Ok(value) => Ok(value)
Err(error) => Err(map_legacy_parse_error(input, error))
}
}
let close = match find_extended_group_close(input) {
Ok(index) => index
Err(error) => return Err(error)
}
for index = close + 1; index < chars.length(); index = index + 1 {
if chars[index] == '(' || chars[index] == ')' {
return Err(
extended_structure_error(input, index, MalformedParenthesesCode),
)
}
}
let group_text = normalized_substring(input.normalized, 1, close)
match interpret_normalized_value(group_text) {
Some(value) if value > 3999 => return Err(RomanReportOutOfRange(value))
_ => ()
}
let group = match parse_extended_segment(input, 1, close) {
Ok(value) => value
Err(error) => return Err(error)
}
let remainder = if close + 1 == chars.length() {
0
} else {
match parse_extended_segment(input, close + 1, chars.length()) {
Ok(value) => value
Err(error) => return Err(error)
}
}
let total = group * 1000 + remainder
if total > 3999999 {
Err(RomanReportOutOfRange(total))
} else {
Ok(total)
}
}