///|
fn value_byte_allowed(b : Byte, p : SyntaxProfile) -> Bool {
if b < b' ' || b > b'~' {
return false
}
if p.charset == "UNOB" {
return true
}
(b >= b'A' && b <= b'Z') ||
(b >= b'0' && b <= b'9') ||
b == b' ' ||
b == b'.' ||
b == b',' ||
b == b'-' ||
b == b'(' ||
b == b')' ||
b == b'/' ||
b == b'=' ||
b == p.component ||
b == p.element ||
b == p.decimal ||
b == p.release ||
b == p.terminator
}
///|
fn released_byte(b : Byte, p : SyntaxProfile) -> Bool {
b == p.component ||
b == p.element ||
b == p.decimal ||
b == p.release ||
b == p.terminator
}
///|
fn scan_segment(
input : Bytes,
start : Int,
p : SyntaxProfile,
limits : ParseLimits,
index : Int,
) -> Result[(Segment, Int), Diagnostic] {
let len = input.length()
if len - start < 4 {
return Err(
diagnostic(
"S_SEGMENT_UNTERMINATED",
Syntax,
start,
len,
"A segment requires a tag and terminator.",
segment_index=index,
),
)
}
for offset = 0; offset < 3; offset = offset + 1 {
let b = input[start + offset]
if b < b'A' || b > b'Z' {
return Err(
diagnostic(
"S_TAG_INVALID",
Syntax,
start,
start + 3,
"Segment tags must have exactly three uppercase letters.",
segment_index=index,
),
)
}
}
if input[start + 3] != p.element && input[start + 3] != p.terminator {
return Err(
diagnostic(
"S_TAG_INVALID",
Syntax,
start,
start + 4,
"Expected a data separator or terminator after the tag.",
segment_index=index,
),
)
}
if limits.max_segment_bytes < 4 {
return Err(
diagnostic(
"R_SEGMENT_BYTES",
Resource,
start,
start + 4,
"Segment exceeds the configured byte budget.",
segment_index=index,
),
)
}
let tag = ascii_text(input, start, start + 3)
let elements : Array[Element] = []
let mut pos = start + 3
if input[pos] == p.terminator {
return Ok(
({ tag, index, elements, span: { start, end: pos + 1, }, }, pos + 1),
)
}
pos = pos + 1
while pos <= len {
if elements.length() >= limits.max_elements {
return Err(
diagnostic(
"R_ELEMENTS",
Resource,
pos,
pos,
"Data element count exceeds the configured budget.",
segment_index=index,
),
)
}
let element_start = pos
let components : Array[Component] = []
let mut component_start = pos
let mut value = @buffer.Buffer()
while true {
if pos >= len {
return Err(
diagnostic(
"S_SEGMENT_UNTERMINATED",
Syntax,
start,
len,
"Segment terminator is missing.",
segment_index=index,
),
)
}
if pos - start >= limits.max_segment_bytes {
return Err(
diagnostic(
"R_SEGMENT_BYTES",
Resource,
start,
pos + 1,
"Segment exceeds the configured byte budget.",
segment_index=index,
),
)
}
let b = input[pos]
if b == p.release {
if len - pos < 2 {
return Err(
diagnostic(
"S_RELEASE_TRUNCATED",
Syntax,
pos,
pos + 1,
"Release character has no following byte.",
segment_index=index,
),
)
}
if pos + 1 - start >= limits.max_segment_bytes {
return Err(
diagnostic(
"R_SEGMENT_BYTES",
Resource,
start,
pos + 2,
"Segment exceeds the configured byte budget.",
segment_index=index,
),
)
}
if !released_byte(input[pos + 1], p) {
return Err(
diagnostic(
"S_RELEASE_INVALID",
Syntax,
pos,
pos + 2,
"Release must precede a service character.",
segment_index=index,
),
)
}
value.write_byte(input[pos + 1])
pos = pos + 2
continue
}
if b == p.component || b == p.element || b == p.terminator {
if components.length() >= limits.max_components {
return Err(
diagnostic(
"R_COMPONENTS",
Resource,
component_start,
pos,
"Component count exceeds the configured budget.",
segment_index=index,
),
)
}
components.push({
value: value.to_bytes(),
span: { start: component_start, end: pos, },
})
if b == p.component {
pos = pos + 1
if components.length() >= limits.max_components {
return Err(
diagnostic(
"R_COMPONENTS",
Resource,
pos,
pos,
"Component count exceeds the configured budget.",
segment_index=index,
),
)
}
component_start = pos
value = @buffer.Buffer()
continue
}
elements.push({ components, span: { start: element_start, end: pos, }, })
pos = pos + 1
if b == p.terminator {
return Ok(
({ tag, index, elements, span: { start, end: pos, }, }, pos),
)
}
break
}
if !value_byte_allowed(b, p) {
return Err(
diagnostic(
"S_CHARACTER_INVALID",
Syntax,
pos,
pos + 1,
"Byte is outside the supported character set.",
segment_index=index,
),
)
}
value.write_byte(b)
pos = pos + 1
}
}
Err(
diagnostic(
"S_SEGMENT_UNTERMINATED",
Syntax,
start,
len,
"Segment terminator is missing.",
segment_index=index,
),
)
}