Skip to content

Commit c7b8647

Browse files
committed
Lexer: Added support for scienific notation to parse doubles
Added can-parse: utilitary tool that parses and outputs to the console a database's content Parses BS_ (but doesn't do anything with it) Fix global comments where not correctly parsed (parsed but not handled) Added comment to CANFrame API
1 parent 97a7f22 commit c7b8647

File tree

7 files changed

+178
-80
lines changed

7 files changed

+178
-80
lines changed

CMakeLists.txt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ add_library(cpp_can_parser STATIC ${CPPPARSER_SRC_FILES})
2020
target_include_directories(cpp_can_parser
2121
PUBLIC ${CPPPARSER_INCLUDE_DIRECTORY})
2222

23-
add_executable(dbcLoadFileExec
24-
tests/dbcLoadFileTest.cpp)
25-
target_link_libraries(dbcLoadFileExec cpp_can_parser)
26-
27-
add_test(NAME dbcLoadFileTest
28-
COMMAND dbcLoadFileExec)
23+
add_executable(can-parse
24+
utils/can-parse.cpp)
25+
target_link_libraries(can-parse cpp_can_parser)

include/Token.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Token {
3737
}
3838

3939
double toDouble() const {
40-
return std::stof(image_);
40+
return std::stod(image_);
4141
}
4242

4343
private:

src/models/CANFrame.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ unsigned int CANFrame::period() const {
4848
return period_;
4949
}
5050

51+
const std::string& CANFrame::comment() const {
52+
return comment_;
53+
}
54+
5155
void CANFrame::setPeriod(unsigned int val) {
5256
period_ = val;
5357
}

src/parsing/DBCParser.cpp

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ using namespace DBCParser;
1010
std::shared_ptr<CANSignal> parseSignal(Tokenizer& tokenizer);
1111
std::shared_ptr<CANFrame> parseFrame(Tokenizer& tokenizer);
1212
std::set<std::string> parseECUs(Tokenizer& tokenizer);
13+
void parseNewSymbols(Tokenizer& tokenizer);
1314
void addBADirective(Tokenizer& tokenizer,
1415
CANDatabase& db);
1516
void addComment(Tokenizer& tokenizer, CANDatabase& db);
@@ -23,11 +24,20 @@ void addComment(Tokenizer& tokenizer, CANDatabase& db) {
2324
Token targetSignal;
2425

2526
assertToken(tokenizer, "CM_");
26-
commentType = checkTokenType(tokenizer, Token::Identifier);
27+
28+
// Handle global comment
29+
Token currentToken = tokenizer.getNextToken();
30+
if (currentToken.type() == Token::Literal) {
31+
skipIf(tokenizer, ";");
32+
return;
33+
}
34+
35+
commentType = checkCurrentTokenType(currentToken, Token::Identifier, tokenizer.lineCount());
2736

2837
auto wWrongFrameId = [](const std::string& fId, unsigned long long line) {
2938
warning("Frame " + fId + " does not exist", line);
3039
};
40+
3141
if(commentType.image() == "SG_") {
3242
targetFrame = checkTokenType(tokenizer, Token::Number);
3343
targetSignal = checkTokenType(tokenizer, Token::Identifier);
@@ -75,7 +85,7 @@ void addBADirective(Tokenizer& tokenizer, CANDatabase& db) {
7585
assertToken(tokenizer, "BA_");
7686

7787
infoType = checkTokenType(tokenizer, Token::Literal);
78-
if(infoType.image() == "GenMsgCycleTime") {
88+
if(infoType.image() == "GenMsgCycleTime" || infoType.image() == "CycleTime") {
7989
skipIf(tokenizer, "BO_");
8090
Token frameId = checkTokenType(tokenizer, Token::Number);
8191
Token period = checkTokenType(tokenizer, Token::Number);
@@ -92,16 +102,18 @@ void addBADirective(Tokenizer& tokenizer, CANDatabase& db) {
92102
return;
93103
}
94104

95-
unsigned int iFrameId = std::stoul(frameId.image());
96-
unsigned int iPeriod = std::stoul(period.image());
105+
unsigned int iFrameId = frameId.toUInt();
106+
unsigned int iPeriod = period.toUInt();
97107

98-
auto frame = db.at(iFrameId);
99-
if(!frame) {
100-
std::cout << "WARNING: frame " << iPeriod << " does not exist "
101-
<< "at line " << tokenizer.lineCount()
102-
<< std::endl;
103-
return;
108+
std::shared_ptr<CANFrame> frame;
109+
try {
110+
frame = db.at(iFrameId);
104111
}
112+
catch (const std::out_of_range& e) {
113+
std::string tempStr = std::to_string(iFrameId) + " does not exist at line " + std::to_string(tokenizer.lineCount());
114+
throw CANDatabaseException(tempStr);
115+
}
116+
105117
frame->setPeriod(iPeriod);
106118
}
107119
else {
@@ -116,6 +128,23 @@ CANDatabase DBCParser::fromTokenizer(Tokenizer& tokenizer) {
116128
return fromTokenizer("", tokenizer);
117129
}
118130

131+
void parseNewSymbols(Tokenizer& tokenizer) {
132+
static std::set<std::string> ns_choices = {
133+
"CM_", "BA_DEF_", "BA_", "VAL_", "CAT_DEF_", "CAT_", "FILTER", "BA_DEF_DEF_",
134+
"EV_DATA_", "ENVVAR_DATA", "SGTYPE_", "SGTYPE_VAL_", "BA_DEF_SGTYPE_", "BA_SGTYPE_",
135+
"SIG_TYPE_DEF_"
136+
};
137+
138+
assertToken(tokenizer, "NS_");
139+
skipIf(tokenizer, ":");
140+
141+
Token token = tokenizer.getNextToken();
142+
while (ns_choices.find(token.image()) != ns_choices.end()) {
143+
token = tokenizer.getNextToken();
144+
}
145+
tokenizer.saveToken(token);
146+
}
147+
119148
CANDatabase DBCParser::fromTokenizer(const std::string& name, Tokenizer& tokenizer) {
120149
std::cout << "Parsing: " << std::endl;
121150
Token currentToken = tokenizer.getNextToken();
@@ -124,37 +153,59 @@ CANDatabase DBCParser::fromTokenizer(const std::string& name, Tokenizer& tokeniz
124153

125154
while(currentToken.type() != Token::Eof) {
126155
// std::cout << currentToken.image() << std::endl;
127-
if(currentToken.image() == "VERSION") {
156+
if (currentToken.image() == "VERSION") {
128157
currentToken = checkTokenType(tokenizer, Token::Literal);
129158
std::cout << "DBC version: " << currentToken.image() << std::endl;
130159
}
131-
else if(currentToken.image() == "BU_") {
160+
else if (currentToken.image() == "BU_") {
132161
std::set<std::string> ecus = parseECUs(tokenizer);
133162
std::cout << "The following ECUs have been defined:" << std::endl;
134-
for(const auto& ecu : ecus) {
163+
for (const auto& ecu : ecus) {
135164
std::cout << ecu << ", ";
136165
}
137166
std::cout << std::endl;
138167
}
139-
else if(currentToken.image() == "BO_") {
168+
else if (currentToken.image() == "BO_") {
140169
std::shared_ptr<CANFrame> frame = parseFrame(tokenizer);
141170
result.addFrame(frame);
142171
}
143-
else if(currentToken.image() == "SG_") {
172+
else if (currentToken.image() == "SG_") {
144173
parseSignal(tokenizer);
145-
std::cout << "Identified signal outside frame -> WARNING !!!"
146-
<< std::endl;
174+
std::cout << "Identified signal outside frame -> WARNING !!! (line "
175+
<< tokenizer.lineCount() << ")" << std::endl;
147176
}
148-
else if(currentToken.image() == "CM_") {
177+
else if (currentToken.image() == "CM_") {
149178
addComment(tokenizer, result);
150179
// TODO: Handle comments
151180
}
152-
else if(currentToken.image() == "BA_") {
181+
else if (currentToken.image() == "BA_") {
153182
addBADirective(tokenizer, result);
154183
}
155-
else if(currentToken.image() == "VAL_") {
184+
else if (currentToken.image() == "VAL_") {
156185
parseSignalChoices(tokenizer, result);
157186
}
187+
else if (currentToken.image() == "NS_") {
188+
parseNewSymbols(tokenizer);
189+
}
190+
else if (currentToken.image() == "BS_") {
191+
skipIf(tokenizer, ":");
192+
193+
currentToken = tokenizer.getNextToken();
194+
if (currentToken.type() != Token::Number)
195+
continue;
196+
197+
Token baudrate = checkCurrentTokenType(currentToken, Token::Number, tokenizer.lineCount());
198+
skipIf(tokenizer, ":");
199+
Token btr1 = checkTokenType(tokenizer, Token::Number);
200+
skipIf(tokenizer, ",");
201+
Token btr2 = checkTokenType(tokenizer, Token::Number);
202+
203+
// TODO: handle the statement
204+
}
205+
else {
206+
std::cerr << currentToken.image() << " is not a valid statement (yet). The statement is skipped." << std::endl;
207+
tokenizer.skipUntil(";");
208+
}
158209
currentToken = tokenizer.getNextToken();
159210
}
160211

@@ -187,7 +238,16 @@ std::shared_ptr<CANSignal> parseSignal(Tokenizer& tokenizer) {
187238
max = checkTokenType(tokenizer, Token::Number);
188239
skipIf(tokenizer, "]");
189240
unit = checkTokenType(tokenizer, Token::Literal);
241+
190242
targetECU = checkTokenType(tokenizer, Token::Identifier); // Ignored for now
243+
Token currentToken = tokenizer.getNextToken();
244+
while (currentToken.image() == ",") {
245+
targetECU = checkTokenType(tokenizer, Token::Identifier);
246+
currentToken = tokenizer.getNextToken();
247+
}
248+
249+
if (currentToken.type() != Token::Eof)
250+
tokenizer.saveToken(currentToken);
191251

192252
return std::make_shared<CANSignal>(
193253
signalName.image(),
@@ -219,8 +279,8 @@ std::shared_ptr<CANFrame> parseFrame(Tokenizer& tokenizer) {
219279

220280
std::shared_ptr<CANFrame> result = std::make_shared<CANFrame>(
221281
name.image(),
222-
std::stoul(id.image()),
223-
std::stoul(dlc.image())
282+
id.toUInt(),
283+
dlc.toUInt()
224284
);
225285

226286
Token currentToken = tokenizer.getNextToken();

src/parsing/Tokenizer.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,13 @@ Token Tokenizer::getNextToken() {
119119
std::string literal = std::string(1, currentChar);
120120
currentChar = getNextChar();
121121

122-
while (isDigit(currentChar) || currentChar == '.') {
122+
while (isDigit(currentChar) || currentChar == '.' || currentChar == 'e') {
123123
literal += currentChar;
124+
125+
if (currentChar == 'e') {
126+
literal += getNextChar();
127+
}
128+
124129
currentChar = getNextChar();
125130
}
126131

tests/dbcLoadFileTest.cpp

Lines changed: 0 additions & 50 deletions
This file was deleted.

utils/can-parse.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include "CANDatabase.h"
2+
3+
#include <iostream>
4+
#include <windows.h>
5+
6+
void printFrame(std::shared_ptr<CANFrame> frame) {
7+
auto choicesStr = [](const std::map<unsigned int, std::string>& choices) {
8+
std::string result = "";
9+
for(const auto& choice: choices) {
10+
result += std::to_string(choice.first) + " -> \"" + choice.second + "\", ";
11+
}
12+
return result;
13+
};
14+
15+
std::cout << "FRAME[" << frame->name() << "]:" << std::endl
16+
<< "- ID(0x" << std::hex << frame->can_id() << std::dec << ")" << std::endl
17+
<< "- DLC(" << frame->dlc() << ")" << std::endl
18+
<< "- PERIOD(" << frame->period() << ")" << std::endl
19+
<< "- COMMENT(" << frame->comment() << ")" << std::endl;
20+
21+
for(const auto& sigw : *frame) {
22+
std::shared_ptr<CANSignal> sig = sigw.second;
23+
std::cout << "SIGNAL[" << sig->name() << "]: "
24+
<< "startBit(" << sig->start_bit() << ") "
25+
<< "length(" << sig->length() << ") "
26+
<< "endianness("
27+
<< ((sig->endianness() == CANSignal::BigEndian) ? "BigEndian" : "LittleEndian")
28+
<< ") "
29+
<< "signedness("
30+
<< ((sig->signedness() == CANSignal::Signed) ? "Signed" : "Unsigned")
31+
<< ") "
32+
<< "scale(" << sig->scale() << ") "
33+
<< "offset(" << sig->offset() << ") "
34+
<< "range(" << sig->range().min << " -> " << sig->range().max << ") "
35+
<< "choices(" << choicesStr(sig->choices()) << ") "
36+
<< std::endl;
37+
}
38+
}
39+
40+
void showUsage(char* program_name) {
41+
std::cerr << "Usage: " << program_name << " <path/to/file>" << std::endl;
42+
std::cerr << "Currently supported formats: DBC" << std::endl;
43+
}
44+
std::string ExePath() {
45+
char buffer[MAX_PATH];
46+
GetModuleFileName(NULL, buffer, MAX_PATH);
47+
std::string::size_type pos = std::string(buffer).find_last_of("\\/");
48+
return std::string(buffer).substr(0, pos);
49+
}
50+
51+
int main(int argc, char** argv) {
52+
if (argc < 2) {
53+
showUsage(argv[0]);
54+
return 1;
55+
}
56+
57+
if (strcmp(argv[1], "-h") == 0) {
58+
showUsage(argv[0]);
59+
return 0;
60+
}
61+
62+
63+
try {
64+
CANDatabase db = CANDatabase::fromFile(argv[1]);
65+
66+
std::cout << "Exploring the CAN Database \"" << db.filename() << "\" "
67+
<< "(size= " << db.size() << "):"
68+
<< std::endl;
69+
70+
for (const auto& framew : db) {
71+
printFrame(framew.second);
72+
}
73+
}
74+
catch (const CANDatabaseException& e) {
75+
std::cerr << "An error happened while parsing the database: "
76+
<< e.what() << std::endl;
77+
std::cerr << "CWD: " << ExePath() << std::endl;
78+
return 1;
79+
}
80+
81+
return 0;
82+
}

0 commit comments

Comments
 (0)