///|
pub(all) struct DbcParseLimits {
max_input_bytes : Int
max_lines : Int
max_messages : Int
max_signals_per_message : Int
max_nodes : Int
}
///|
pub fn DbcParseLimits::standard() -> DbcParseLimits {
{
max_input_bytes: 4 * 1024 * 1024,
max_lines: 100000,
max_messages: 10000,
max_signals_per_message: 1000,
max_nodes: 10000,
}
}
///|
fn parse_error(
code : DbcErrorCode,
message : String,
line : Int,
context : String,
) -> DbcError {
DbcError::new(code, message, line, 1, context~)
}
///|
fn parse_version_line(line : String, number : Int) -> Result[String, DbcError] {
let cursor = TextCursor::new(line, number)
if !cursor.consume_text("VERSION") {
return Err(parse_error(InvalidSyntax, "expected VERSION", number, line))
}
let version = match cursor.quoted() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.finish() {
Ok(_) => Ok(version)
Err(error) => Err(error)
}
}
///|
fn parse_nodes_line(
line : String,
number : Int,
) -> Result[Array[DbcNode], DbcError] {
let cursor = TextCursor::new(line, number)
if !cursor.consume_text("BU_") {
return Err(parse_error(InvalidSyntax, "expected BU_", number, line))
}
match cursor.expect_char(58, "colon after BU_") {
Err(error) => return Err(error)
Ok(_) => ()
}
let nodes : Array[DbcNode] = []
cursor.skip_spaces()
while !cursor.at_end() {
let name = match cursor.identifier() {
Ok(value) => value
Err(error) => return Err(error)
}
nodes.push(DbcNode::new(name))
cursor.skip_spaces()
}
Ok(nodes)
}
///|
fn parse_message_line(
line : String,
number : Int,
) -> Result[DbcMessage, DbcError] {
let cursor = TextCursor::new(line, number)
if !cursor.consume_text("BO_") {
return Err(parse_error(InvalidMessage, "expected BO_", number, line))
}
let id = match cursor.integer() {
Ok(value) => value
Err(error) => return Err(error)
}
let name = match cursor.identifier() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(58, "colon after message name") {
Err(error) => return Err(error)
Ok(_) => ()
}
let size = match cursor.integer() {
Ok(value) => value
Err(error) => return Err(error)
}
let transmitter = match cursor.identifier() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.finish() {
Err(error) => return Err(error)
Ok(_) => ()
}
if id < 0 {
return Err(
parse_error(InvalidMessage, "message id cannot be negative", number, line),
)
}
if size < 0 || size > 64 {
return Err(
parse_error(
InvalidMessage,
"message size must be between 0 and 64 bytes",
number,
line,
),
)
}
Ok(DbcMessage::new(id, name, size, transmitter, [], source_line=number))
}
///|
fn parse_receivers(cursor : TextCursor) -> Result[Array[String], DbcError] {
let receivers : Array[String] = []
cursor.skip_spaces()
if cursor.at_end() {
return Ok(receivers)
}
while !cursor.at_end() {
let receiver = match cursor.identifier() {
Ok(value) => value
Err(error) => return Err(error)
}
receivers.push(receiver)
cursor.skip_spaces()
if cursor.at_end() {
break
}
if !cursor.consume_char(44) {
return Err(
cursor_error(cursor, InvalidSignal, "expected comma between receivers"),
)
}
}
Ok(receivers)
}
///|
fn parse_signal_line(
line : String,
number : Int,
) -> Result[DbcSignal, DbcError] {
let cursor = TextCursor::new(line, number)
if !cursor.consume_text("SG_") {
return Err(parse_error(InvalidSignal, "expected SG_", number, line))
}
let name = match cursor.identifier() {
Ok(value) => value
Err(error) => return Err(error)
}
cursor.skip_spaces()
if cursor.peek() == 77 || cursor.peek() == 109 {
return Err(
cursor_error(
cursor,
UnsupportedConstruct,
"multiplexed signals are not supported by version 0.1.0",
),
)
}
match cursor.expect_char(58, "colon after signal name") {
Err(error) => return Err(error)
Ok(_) => ()
}
let start_bit = match cursor.integer() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(124, "vertical bar after signal start bit") {
Err(error) => return Err(error)
Ok(_) => ()
}
let bit_length = match cursor.integer() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(64, "at sign before byte order") {
Err(error) => return Err(error)
Ok(_) => ()
}
let order_code = match cursor.integer() {
Ok(value) => value
Err(error) => return Err(error)
}
let byte_order = match order_code {
0 => Motorola
1 => Intel
_ =>
return Err(
cursor_error(cursor, InvalidSignal, "byte order must be 0 or 1"),
)
}
cursor.skip_spaces()
let value_type = if cursor.consume_char(43) {
Unsigned
} else if cursor.consume_char(45) {
Signed
} else {
return Err(
cursor_error(cursor, InvalidSignal, "signal value type must be + or -"),
)
}
match cursor.expect_char(40, "opening parenthesis before scaling") {
Err(error) => return Err(error)
Ok(_) => ()
}
let factor = match cursor.number() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(44, "comma between factor and offset") {
Err(error) => return Err(error)
Ok(_) => ()
}
let offset = match cursor.number() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(41, "closing parenthesis after scaling") {
Err(error) => return Err(error)
Ok(_) => ()
}
match cursor.expect_char(91, "opening bracket before range") {
Err(error) => return Err(error)
Ok(_) => ()
}
let minimum = match cursor.number() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(124, "vertical bar between range bounds") {
Err(error) => return Err(error)
Ok(_) => ()
}
let maximum = match cursor.number() {
Ok(value) => value
Err(error) => return Err(error)
}
match cursor.expect_char(93, "closing bracket after range") {
Err(error) => return Err(error)
Ok(_) => ()
}
let unit = match cursor.quoted() {
Ok(value) => value
Err(error) => return Err(error)
}
let receivers = match parse_receivers(cursor) {
Ok(value) => value
Err(error) => return Err(error)
}
if start_bit < 0 || bit_length <= 0 || bit_length > 64 {
return Err(
parse_error(
InvalidSignal,
"signal bit layout is outside supported bounds",
number,
line,
),
)
}
if factor == 0.0 {
return Err(
parse_error(InvalidSignal, "signal factor cannot be zero", number, line),
)
}
if minimum > maximum {
return Err(
parse_error(InvalidSignal, "signal minimum exceeds maximum", number, line),
)
}
Ok(
DbcSignal::new(
name,
start_bit,
bit_length,
byte_order,
value_type,
factor,
offset,
minimum,
maximum,
unit,
receivers,
source_line=number,
),
)
}
///|
fn is_ignored_dbc_statement(line : String) -> Bool {
line == "BS_:" ||
line == "BS_ :" ||
line.has_prefix("NS_") ||
line.has_prefix("NS_DESC_") ||
line.has_prefix("BA_DEF_") ||
line.has_prefix("BA_DEF_DEF_") ||
line.has_prefix("BA_") ||
line.has_prefix("SIG_VALTYPE_") ||
line.has_prefix("EV_") ||
line.has_prefix("ENVVAR_DATA_") ||
line.has_prefix("SGTYPE_") ||
line.has_prefix("SIG_TYPE_REF_") ||
line.has_prefix("BO_TX_BU_") ||
line.has_prefix("SG_MUL_VAL_") ||
line.has_prefix("CAT_") ||
line.has_prefix("FILTER")
}
///|
pub fn parse_dbc(
text : String,
limits? : DbcParseLimits = DbcParseLimits::standard(),
) -> Result[DbcDatabase, DbcError] {
if text.trim().is_empty() {
return Err(DbcError::new(EmptyInput, "DBC input is empty", 1, 1))
}
if limits.max_input_bytes < 0 ||
limits.max_lines < 0 ||
limits.max_messages < 0 ||
limits.max_signals_per_message < 0 ||
limits.max_nodes < 0 {
return Err(
DbcError::new(InputLimitExceeded, "parse limits cannot be negative", 1, 1),
)
}
if text.length() > limits.max_input_bytes {
return Err(
DbcError::new(
InputLimitExceeded,
"DBC input exceeds configured byte limit",
1,
1,
),
)
}
let mut version = ""
let nodes : Array[DbcNode] = []
let messages : Array[DbcMessage] = []
let mut current_message = -1
let mut ignored = 0
let mut line_number = 0
for line_view in text.split("\n") {
line_number = line_number + 1
if line_number > limits.max_lines {
return Err(
DbcError::new(
LineLimitExceeded,
"DBC input exceeds configured line limit",
line_number,
1,
),
)
}
let line = line_view.trim().to_owned()
if line == "" {
continue
}
if line.has_prefix("VERSION") {
version = match parse_version_line(line, line_number) {
Ok(value) => value
Err(error) => return Err(error)
}
} else if line.has_prefix("BU_") {
let parsed = match parse_nodes_line(line, line_number) {
Ok(value) => value
Err(error) => return Err(error)
}
if nodes.length() + parsed.length() > limits.max_nodes {
return Err(
parse_error(
InputLimitExceeded,
"node count exceeds configured limit",
line_number,
line,
),
)
}
for node in parsed {
nodes.push(node)
}
} else if line.has_prefix("BO_") && !line.has_prefix("BO_TX_BU_") {
if messages.length() >= limits.max_messages {
return Err(
parse_error(
InputLimitExceeded,
"message count exceeds configured limit",
line_number,
line,
),
)
}
let message = match parse_message_line(line, line_number) {
Ok(value) => value
Err(error) => return Err(error)
}
messages.push(message)
current_message = messages.length() - 1
} else if line.has_prefix("SG_") &&
!line.has_prefix("SGTYPE_") &&
!line.has_prefix("SG_MUL_VAL_") {
if current_message < 0 {
return Err(
parse_error(
InvalidSignal,
"signal appears before any message",
line_number,
line,
),
)
}
let signal = match parse_signal_line(line, line_number) {
Ok(value) => value
Err(error) => return Err(error)
}
let message = messages[current_message]
let signals = message.signals()
if signals.length() >= limits.max_signals_per_message {
return Err(
parse_error(
InputLimitExceeded,
"signal count exceeds configured limit",
line_number,
line,
),
)
}
signals.push(signal)
messages[current_message] = message.with_signals(signals)
} else if line.has_prefix("CM_") {
let handled = match
parse_comment_annotation(line, line_number, messages) {
Ok(value) => value
Err(error) => return Err(error)
}
if !handled {
ignored = ignored + 1
}
} else if line.has_prefix("VAL_") {
match parse_value_annotation(line, line_number, messages) {
Ok(_) => ()
Err(error) => return Err(error)
}
} else if is_ignored_dbc_statement(line) {
ignored = ignored + 1
} else {
return Err(
parse_error(
UnsupportedConstruct,
"unsupported DBC statement",
line_number,
line,
),
)
}
}
Ok(
DbcDatabase::new(version, nodes, messages, ignored_statement_count=ignored),
)
}