///|
priv struct Cursor {
data : String
/// The current position in the data
mut pos : UInt64
}
///|
fn Cursor::new(data : String) -> Cursor {
{ data, pos: 0 }
}
///|
fn Cursor::read_line(self : Cursor) -> String {
if self.peek().is_empty() {
return ""
}
let result = StringBuilder::new()
while true {
guard self.next() is Some(byte) else {
result.write_char('\n')
break
}
if byte == '\n'.to_int() {
result.write_char('\n')
break
}
if byte == '\r'.to_int() {
// Check if the next byte is a newline(CRLF)
let next = self.peek()
if next == Some('\n') {
// Skip the newline byte
ignore(self.next())
}
result.write_char('\n')
break
}
result.write_char(byte.unsafe_to_char())
}
// Convert the result to a string
result.to_string()
}
///|
fn Cursor::length(self : Cursor) -> UInt64 {
self.data.length().to_uint64()
}
///|
fn Cursor::next(self : Cursor) -> Int? {
if self.pos >= self.data.length().to_uint64() {
None
} else {
let char = self.data[self.pos.to_int()]
self.pos += 1
Some(char)
}
}
///|
fn Cursor::peek(self : Cursor) -> Int? {
if self.pos >= self.data.length().to_uint64() {
None
} else {
Some(self.data[self.pos.to_int()])
}
}
///|
fn Cursor::pos(self : Cursor) -> UInt64 {
self.pos
}
///|
fn Cursor::set_pos(self : Cursor, pos : UInt64) -> Unit {
self.pos = pos
}
///|
fn Cursor::next_line(self : Cursor) -> Result[String, ParseBufError]? {
for {
let mut buf = ""
let mut cur_state = ParseState::Complete
let mut buf_pos : UInt = 0
let mut cur_pos : UInt = 0
for {
buf_pos = buf.length().reinterpret_as_uint()
let new_line = self.read_line()
buf = buf + new_line
match new_line {
"" => {
// End of file
if cur_state == ParseState::Complete {
return None
}
let len = buf.length()
return Some(
Err(ParseBufError::LineParse(buf, len.reinterpret_as_uint())),
)
}
_ => {
// Skip lines which start with a `#` before iteration
// This optimizes parsing a bit.
if buf.trim_space().to_string().has_prefix("#") {
return Some(Ok(""))
}
let result = cur_state.eval_end(
buf.view(start_offset=buf_pos.reinterpret_as_int()).to_string(),
)
cur_pos = result.0
cur_state = result.1
match cur_state {
ParseState::Complete =>
return Some(
Ok(
// TODO: Performance optimization
buf.trim_end("\n").trim_end("\r").to_string(),
),
)
ParseState::Escape
| ParseState::StrongOpen
| ParseState::StrongOpenEscape
| ParseState::WeakOpen
| ParseState::WeakOpenEscape
| ParseState::WhiteSpace => ()
ParseState::Comment =>
return Some(
Ok(
buf
.view(
start_offset=0,
end_offset=(buf_pos + cur_pos).reinterpret_as_int(),
)
.to_string(),
),
)
}
}
}
}
}
}