///|
priv struct Parser {
tokens : Array[Token]
mut index : Int
limits : ParseLimits
}
///|
fn Parser::peek(self : Parser) -> Token {
self.tokens[self.index]
}
///|
fn Parser::error(
self : Parser,
code : String,
message : String,
) -> Unit raise DbcError {
raise Failure(diagnostic(code, message, location=self.peek().location))
}
///|
fn Parser::take(self : Parser) -> Token raise DbcError {
let t = self.peek()
if t.text == "" && !t.quoted {
self.error("parse.eof", "unexpected end of input")
}
self.index += 1
t
}
///|
fn Parser::skip_lines(self : Parser) -> Unit {
while self.peek().text == "\n" && !self.peek().quoted {
self.index += 1
}
}
///|
fn Parser::expect(self : Parser, text : String) -> Unit raise DbcError {
let t = self.take()
if t.quoted || t.text != text {
raise Failure(
diagnostic(
"parse.expected",
"expected \{text}, got \{t.text}",
location=t.location,
),
)
}
}
///|
fn Parser::quoted(self : Parser) -> String raise DbcError {
let t = self.take()
if !t.quoted {
raise Failure(
diagnostic(
"parse.string",
"expected a quoted string",
location=t.location,
),
)
}
t.text
}
///|
fn valid_name(name : String) -> Bool {
if name.length() == 0 {
return false
}
for i = 0; i < name.length(); i = i + 1 {
let c = name[i].to_int()
let letter = (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c == 95
if !letter && !(i > 0 && c >= 48 && c <= 57) {
return false
}
}
true
}
///|
fn Parser::name(self : Parser) -> String raise DbcError {
let t = self.take()
if t.quoted || !valid_name(t.text) {
raise Failure(
diagnostic("parse.name", "invalid identifier", location=t.location),
)
}
if t.text.length() > self.limits.max_name_length {
raise Failure(
diagnostic("limit.name", "identifier exceeds budget", location=t.location),
)
}
t.text
}
///|
fn Parser::number_text(self : Parser) -> String raise DbcError {
let mut prefix = ""
if self.peek().text == "-" || self.peek().text == "+" {
let sign = self.take()
if sign.quoted {
raise Failure(
diagnostic(
"parse.number",
"quoted numeric sign",
location=sign.location,
),
)
}
prefix = sign.text
}
let t = self.take()
if t.quoted {
self.error("parse.number", "quoted numeric literal")
}
prefix + t.text
}
///|
fn Parser::integer(self : Parser) -> Int64 raise DbcError {
let location = self.peek().location
let text = self.number_text()
@string.parse_int64(text) catch {
_ =>
raise Failure(
diagnostic(
"parse.integer",
"invalid or overflowing integer: \{text}",
location~,
),
)
}
}
///|
fn Parser::small_integer(self : Parser) -> Int raise DbcError {
let n = self.integer()
if n < 0L || n > 2147483647L {
self.error("parse.integer", "expected nonnegative 32-bit integer")
}
n.to_int()
}
///|
fn Parser::id(self : Parser) -> UInt raise DbcError {
let n = self.integer()
if n < 0L || n > 4294967295L {
self.error("parse.id", "DBC ID must be a 32-bit unsigned integer")
}
n.to_int().reinterpret_as_uint()
}
///|
fn finite(value : Double) -> Bool {
!value.is_nan() && !value.is_inf()
}
///|
fn Parser::real(self : Parser) -> Double raise DbcError {
let location = self.peek().location
let text = self.number_text()
let value = @string.parse_double(text) catch {
_ =>
raise Failure(
diagnostic(
"parse.number",
"invalid floating point literal: \{text}",
location~,
),
)
}
if !finite(value) {
raise Failure(
diagnostic("parse.number", "nonfinite numeric literal", location~),
)
}
value
}
///|
fn Parser::end_line(self : Parser) -> Unit raise DbcError {
let t = self.peek()
if t.text == "\n" && !t.quoted {
self.index += 1
} else if t.text != "" || t.quoted {
self.error("parse.trailing", "unexpected tokens at end of line")
}
}
///|
fn Parser::object_ref(self : Parser) -> ObjectRef raise DbcError {
if self.peek().quoted {
return DatabaseRef
}
match self.peek().text {
"BU_" => {
self.index += 1
NodeRef(self.name())
}
"BO_" => {
self.index += 1
MessageRef(self.id())
}
"SG_" => {
self.index += 1
SignalRef(self.id(), self.name())
}
_ => DatabaseRef
}
}
///|
fn object_scope(target : ObjectRef) -> Scope {
match target {
DatabaseRef => DatabaseScope
NodeRef(_) => NodeScope
MessageRef(_) => MessageScope
SignalRef(_, _) => SignalScope
}
}