///|
priv suberror ParseFailure {
ParseFailure(String, Array[String?])
}
///|
priv struct Cursor {
chars : Array[Char]
mut index : Int32
mut peek_offset : Int32
mut depth : Int32
max_depth : Int32
}
///|
fn Cursor::new(source : String) -> Cursor {
{
chars: source.to_array(),
index: 0,
peek_offset: 0,
depth: 0,
max_depth: 64,
}
}
///|
fn Cursor::raw(self : Cursor, offset : Int32) -> Char? {
if offset >= 0 && offset < self.chars.length() {
Some(self.chars[offset])
} else {
None
}
}
///|
fn Cursor::at(self : Cursor, offset : Int32) -> Char? {
if self.raw(offset) == Some('\r') && self.raw(offset + 1) == Some('\n') {
Some('\n')
} else {
self.raw(offset)
}
}
///|
fn Cursor::current(self : Cursor) -> Char? {
self.at(self.index)
}
///|
fn Cursor::current_peek(self : Cursor) -> Char? {
self.at(self.index + self.peek_offset)
}
///|
fn Cursor::next(self : Cursor) -> Char? {
self.peek_offset = 0
if self.raw(self.index) == Some('\r') &&
self.raw(self.index + 1) == Some('\n') {
self.index += 1
}
self.index += 1
self.raw(self.index)
}
///|
fn Cursor::peek(self : Cursor) -> Char? {
let i = self.index + self.peek_offset
if self.raw(i) == Some('\r') && self.raw(i + 1) == Some('\n') {
self.peek_offset += 1
}
self.peek_offset += 1
self.raw(self.index + self.peek_offset)
}
///|
fn Cursor::reset_peek(self : Cursor, offset~ : Int32 = 0) -> Unit {
self.peek_offset = offset
}
///|
fn Cursor::skip_to_peek(self : Cursor) -> Unit {
self.index += self.peek_offset
self.peek_offset = 0
}
///|
fn Cursor::slice(self : Cursor, start : Int32, end : Int32) -> String {
let buf = StringBuilder()
for i in start.. String {
let start = self.index + self.peek_offset
while self.current_peek() == Some(' ') {
ignore(self.peek())
}
self.slice(start, self.index + self.peek_offset)
}
///|
fn Cursor::skip_blank_inline(self : Cursor) -> String {
let blank = self.peek_blank_inline()
self.skip_to_peek()
blank
}
///|
fn Cursor::peek_blank_block(self : Cursor) -> String {
let buf = StringBuilder()
while true {
let line_start = self.peek_offset
ignore(self.peek_blank_inline())
if self.current_peek() == Some('\n') {
buf.write_char('\n')
ignore(self.peek())
continue
}
if self.current_peek() != None {
self.reset_peek(offset=line_start)
}
break
}
buf.to_string()
}
///|
fn Cursor::skip_blank_block(self : Cursor) -> String {
let blank = self.peek_blank_block()
self.skip_to_peek()
blank
}
///|
fn Cursor::peek_blank(self : Cursor) -> Unit {
while self.current_peek() == Some(' ') || self.current_peek() == Some('\n') {
ignore(self.peek())
}
}
///|
fn Cursor::skip_blank(self : Cursor) -> Unit {
self.peek_blank()
self.skip_to_peek()
}
///|
fn Cursor::expect(self : Cursor, ch : Char) -> Unit raise ParseFailure {
if self.current() != Some(ch) {
raise ParseFailure("E0003", [Some(ch.to_string())])
}
ignore(self.next())
}
///|
fn Cursor::expect_line_end(self : Cursor) -> Unit raise ParseFailure {
if self.current() == None {
return
}
if self.current() == Some('\n') {
ignore(self.next())
return
}
raise ParseFailure("E0003", [Some("\u{2424}")])
}
///|
fn is_id_start(ch : Char?) -> Bool {
match ch {
Some(c) => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
None => false
}
}
///|
fn is_digit(ch : Char?) -> Bool {
match ch {
Some(c) => c >= '0' && c <= '9'
None => false
}
}
///|
fn is_id_char(ch : Char?) -> Bool {
is_id_start(ch) || is_digit(ch) || ch == Some('_') || ch == Some('-')
}
///|
fn is_hex(ch : Char?) -> Bool {
match ch {
Some(c) => is_digit(ch) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
None => false
}
}
///|
fn Cursor::is_number_start(self : Cursor) -> Bool {
let ch = if self.current() == Some('-') {
self.peek()
} else {
self.current()
}
self.reset_peek()
is_digit(ch)
}
///|
fn Cursor::is_value_start(self : Cursor) -> Bool {
self.current_peek() != None && self.current_peek() != Some('\n')
}
///|
fn Cursor::is_value_continuation(self : Cursor) -> Bool {
let column1 = self.peek_offset
ignore(self.peek_blank_inline())
if self.current_peek() == Some('{') {
self.reset_peek(offset=column1)
return true
}
if self.peek_offset == column1 {
return false
}
match self.current_peek() {
None | Some('}') | Some('.') | Some('[') | Some('*') => false
_ => {
self.reset_peek(offset=column1)
true
}
}
}
///|
fn Cursor::is_next_line_comment(self : Cursor, level : Int32) -> Bool {
if self.current() != Some('\n') {
return false
}
ignore(self.peek())
for _ in 0..<(level + 1) {
if self.current_peek() != Some('#') {
self.reset_peek()
return false
}
ignore(self.peek())
}
let result = self.current_peek() == Some(' ') ||
self.current_peek() == Some('\n') ||
self.current_peek() == None
self.reset_peek()
result
}
///|
fn Cursor::is_variant_start(self : Cursor) -> Bool {
let offset = self.peek_offset
if self.current_peek() == Some('*') {
ignore(self.peek())
}
let result = self.current_peek() == Some('[') && self.peek() != Some('[')
self.reset_peek(offset~)
result
}
///|
fn Cursor::recover(self : Cursor, junk_start : Int32) -> Unit {
let mut last_newline = -1
for i in 0..