///|
fn is_space_code(code : Int) -> Bool {
code == 32 || code == 9 || code == 10 || code == 13
}
///|
fn is_newline_code(code : Int) -> Bool {
code == 10
}
///|
fn is_brace_code(code : Int) -> Bool {
code == 123 || code == 125
}
///|
fn token_kind_for_brace(code : Int) -> BvhTokenKind {
if code == 123 {
TokenOpenBrace
} else {
TokenCloseBrace
}
}
///|
fn token_text_for_brace(code : Int) -> String {
if code == 123 {
"{"
} else {
"}"
}
}
///|
/// Split ASCII BVH source into words and braces while preserving token position.
pub fn tokenize_bvh(input : String) -> BvhTokenizeResult {
if input.trim().length() == 0 {
return BvhTokenizeResult::failure(
BvhError::new(ErrorEmptyInput, "BVH source is empty"),
)
}
let tokens : Array[BvhToken] = []
let mut i = 0
let mut line = 1
let mut column = 1
while i < input.length() {
let code = input.unsafe_get(i).to_int()
if is_space_code(code) {
if is_newline_code(code) {
line += 1
column = 1
} else {
column += 1
}
i += 1
} else if is_brace_code(code) {
tokens.push(
BvhToken::new(
token_kind_for_brace(code),
token_text_for_brace(code),
line,
column,
),
)
i += 1
column += 1
} else {
let start = i
let token_line = line
let token_column = column
while i < input.length() {
let word_code = input.unsafe_get(i).to_int()
if is_space_code(word_code) || is_brace_code(word_code) {
break
}
i += 1
column += 1
}
tokens.push(
BvhToken::new(
TokenWord,
input[start:i].to_owned(),
token_line,
token_column,
),
)
}
}
BvhTokenizeResult::success(tokens)
}
///|
pub fn bvh_tokens_to_text(tokens : Array[BvhToken]) -> String {
let parts : Array[String] = []
for token in tokens {
parts.push("\{token.text}@\{token.line}:\{token.column}")
}
parts.join(" ")
}