///|
priv struct Reader {
data : Bytes
limits : Limits
mut pos : Int32
mut line : Int32
mut column : Int32
mut nodes : Int32
mut trace : Bool
locations : Map[String, SourceLocation]
mut current_path : String
}
///|
fn Reader::new(data : Bytes, limits : Limits) -> Reader raise ConfigError {
if limits.max_input < 0 ||
limits.max_depth < 0 ||
limits.max_depth > 256 ||
limits.max_nodes < 0 ||
limits.max_string < 0 {
raise failure(
"invalid_limits", "limits must be nonnegative; max_depth must not exceed 256",
)
}
if data.length() > limits.max_input {
raise failure("input_limit", "input exceeds byte limit")
}
{
data,
limits,
pos: 0,
line: 1,
column: 1,
nodes: 0,
trace: false,
locations: Map([]),
current_path: "",
}
}
///|
fn Reader::peek(self : Reader, ahead~ : Int32 = 0) -> Int32 {
match self.data.get(self.pos + ahead) {
Some(b) => b.to_int()
None => -1
}
}
///|
fn Reader::advance(self : Reader) -> Unit {
if self.peek() == -1 {
return
}
if self.peek() == 10 {
self.line += 1
self.column = 1
} else {
self.column += 1
}
self.pos += 1
}
///|
fn Reader::error(self : Reader, code : String, message : String) -> ConfigError {
ConfigError({
code,
message: if self.trace && self.current_path != "" {
"at " + self.current_path + ": " + message
} else {
message
},
offset: self.pos,
line: self.line,
column: self.column,
})
}
///|
fn Reader::skip(self : Reader) -> Unit raise ConfigError {
while true {
let c = self.peek()
if c == 32 || c == 9 || c == 10 || c == 13 || c == 12 {
self.advance()
} else if c == 35 || (c == 47 && self.peek(ahead=1) == 47) {
while self.peek() != -1 && self.peek() != 10 {
self.advance()
}
} else if c == 47 && self.peek(ahead=1) == 42 {
self.advance()
self.advance()
while !(self.peek() == 42 && self.peek(ahead=1) == 47) {
if self.peek() == -1 {
raise self.error("unterminated_comment", "expected */")
}
self.advance()
}
self.advance()
self.advance()
} else {
break
}
}
}
///|
fn name_start(c : Int32) -> Bool {
(c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c == 42
}
///|
fn name_part(c : Int32) -> Bool {
name_start(c) || (c >= 48 && c <= 57) || c == 45 || c == 95
}
///|
fn Reader::name(self : Reader) -> String raise ConfigError {
if !name_start(self.peek()) {
raise self.error("invalid_name", "expected ASCII setting name")
}
let start = self.pos
while name_part(self.peek()) {
self.advance()
}
@utf8.decode_lossy(self.data[start:self.pos])
}
///|
fn Reader::expect(self : Reader, c : Int32) -> Unit raise ConfigError {
self.skip()
if self.peek() != c {
raise self.error("unexpected_token", "unexpected delimiter")
}
self.advance()
}