/* ; Project: Open Vehicle Monitor System ; Date: 15th November 2018 ; ; Changes: ; 1.0 Initial release ; ; (C) 2011 Michael Stegen / Stegen Electronics ; (C) 2011-2017 Mark Webb-Johnson ; (C) 2011 Sonny Chen @ EPRO/DX ; ; With credit due to candbc-parser.y, for idea and structure ; https://github.com/Polyconseil/libcanardbc ; Copyright (C) 2007-2009 Andreas Heitmann ; ; Permission is hereby granted, free of charge, to any person obtaining a copy ; of this software and associated documentation files (the "Software"), to deal ; in the Software without restriction, including without limitation the rights ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the Software is ; furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included in ; all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ; THE SOFTWARE. */ %{ #include #include #include #include #include #include #include #include #include "dbc.h" #include "ovms_log.h" #ifdef CONFIG_OVMS #define YYMALLOC ExternalRamMalloc #include "ovms_malloc.h" #endif // #ifdef CONFIG_OVMS /* Tell Bison how much stack space is needed. */ #define YYERROR_VERBOSE 1 #define YYMAXDEPTH 1000 %} %parse-param {void* dbcptr} %union { long long number; double double_val; char* string; }; %{ extern int yylex (void); extern char *yytext; extern int yylineno; static const char *TAG = "dbc-parser"; extern "C" { void yyerror(void* dbcptr, const char *msg) { ESP_LOGE(TAG,"Error in line %d '%s', symbol '%s'", yylineno, msg, yytext); } #ifdef CONFIG_OVMS void *yyalloc (size_t sz) { return ExternalRamMalloc(sz); } void *yyrealloc (void *ptr, size_t sz) { return ExternalRamRealloc(ptr,sz); } #endif // #ifdef CONFIG_OVMS } dbcfile* current_dbc = NULL; dbcValueTable* current_value_table = NULL; dbcMessage* current_message = NULL; dbcSignal* current_signal = NULL; %} %token T_COLON %token T_SEMICOLON %token T_SEP %token T_AT %token T_PLUS %token T_MINUS %token T_BOX_OPEN %token T_BOX_CLOSE %token T_PAR_OPEN %token T_PAR_CLOSE %token T_COMMA %token T_ID %token T_STRING_VAL %token T_INT_VAL %token T_DOUBLE_VAL %token T_VERSION %token T_INT %token T_FLOAT %token T_NAN %token T_STRING %token T_ENUM %token T_HEX %token T_BO /* Botschaft */ %token T_BS %token T_BU /* Steuergerät */ %token T_SG /* Signal */ %token T_EV /* Environment */ %token T_NS %token T_NS_DESC %token T_CM /* Comment */ %token T_BA_DEF /* Attribut-Definition */ %token T_BA /* Attribut */ %token T_VAL %token T_CAT_DEF %token T_CAT %token T_FILTER %token T_BA_DEF_DEF %token T_EV_DATA %token T_ENVVAR_DATA %token T_SGTYPE %token T_SGTYPE_VAL %token T_BA_DEF_SGTYPE %token T_BA_SGTYPE %token T_SIG_TYPE_REF %token T_VAL_TABLE %token T_SIG_GROUP %token T_SIG_VALTYPE %token T_SIGTYPE_VALTYPE %token T_BO_TX_BU %token T_BA_DEF_REL %token T_BA_REL %token T_BA_DEF_DEF_REL %token T_BU_SG_REL %token T_BU_EV_REL %token T_BU_BO_REL %token T_SG_MUL_VAL %token T_DUMMY_NODE_VECTOR %type T_ID T_STRING_VAL version_section signal_mux %type T_INT_VAL signal_endian signal_sign signal_start signal_length %type T_DOUBLE_VAL double_val signal_scale signal_offset signal_min signal_max %% dbc: { current_dbc = (dbcfile*)dbcptr; current_value_table = NULL; current_message = NULL; current_signal = NULL; } dbc_sections ; dbc_sections: | dbc_sections dbc_section ; dbc_section: version_section | symbol_section | bit_timing_section | node_list_section | value_table_section | message_section | signal_section | value_section | attribute_section | attribute_default_section | comment_section ; /************************************************************************/ /* version_section (VERSION) */ /************************************************************************/ version_section: T_VERSION T_STRING_VAL { ESP_LOGD(TAG,"VERSION parsed as: %s",$2); current_dbc->m_version = std::string($2); if ($2) { free($2); $2=NULL; } }; /************************************************************************/ /* symbol_section (NS_) */ /************************************************************************/ symbol_section: T_NS T_COLON symbol_list { ESP_LOGD(TAG,"NS_ parsed %d symbols",current_dbc->m_newsymbols.GetCount()); } ; symbol_list: | symbol_list symbol ; symbol: T_NS_DESC { current_dbc->m_newsymbols.AddSymbol("NS_DESC_"); } | T_CM { current_dbc->m_newsymbols.AddSymbol("CM_"); } | T_BA_DEF { current_dbc->m_newsymbols.AddSymbol("BA_DEF_"); } | T_BA { current_dbc->m_newsymbols.AddSymbol("BA_"); } | T_VAL { current_dbc->m_newsymbols.AddSymbol("VAL_"); } | T_CAT_DEF { current_dbc->m_newsymbols.AddSymbol("CAT_DEF_"); } | T_CAT { current_dbc->m_newsymbols.AddSymbol("CAT_"); } | T_FILTER { current_dbc->m_newsymbols.AddSymbol("FILTER"); } | T_BA_DEF_DEF { current_dbc->m_newsymbols.AddSymbol("BA_DEF_DEF_"); } | T_EV_DATA { current_dbc->m_newsymbols.AddSymbol("EV_DATA_"); } | T_ENVVAR_DATA { current_dbc->m_newsymbols.AddSymbol("ENVVAR_DATA_"); } | T_SGTYPE { current_dbc->m_newsymbols.AddSymbol("SG_TYPE_"); } | T_SGTYPE_VAL { current_dbc->m_newsymbols.AddSymbol("SG_TYPE_VAL_"); } | T_BA_DEF_SGTYPE { current_dbc->m_newsymbols.AddSymbol("BA_DEF_SGTYPE_"); } | T_BA_SGTYPE { current_dbc->m_newsymbols.AddSymbol("BA_SGTYPE_"); } | T_SIG_TYPE_REF { current_dbc->m_newsymbols.AddSymbol("SIG_TYPE_REF_"); } | T_VAL_TABLE { current_dbc->m_newsymbols.AddSymbol("VAL_TABLE_"); } | T_SIG_GROUP { current_dbc->m_newsymbols.AddSymbol("SIG_GROUP_"); } | T_SIG_VALTYPE { current_dbc->m_newsymbols.AddSymbol("SIG_VALTYPE_"); } | T_SIGTYPE_VALTYPE { current_dbc->m_newsymbols.AddSymbol("SIGTYPE_VALTYPE_"); } | T_BO_TX_BU { current_dbc->m_newsymbols.AddSymbol("BO_TX_BU_"); } | T_BA_DEF_REL { current_dbc->m_newsymbols.AddSymbol("BA_DEF_REL_"); } | T_BA_REL { current_dbc->m_newsymbols.AddSymbol("BA_REL_"); } | T_BA_DEF_DEF_REL { current_dbc->m_newsymbols.AddSymbol("BA_DEF_DEF_REL_"); } | T_BU_SG_REL { current_dbc->m_newsymbols.AddSymbol("BU_SG_REL_"); } | T_BU_EV_REL { current_dbc->m_newsymbols.AddSymbol("BU_EV_REL_"); } | T_BU_BO_REL { current_dbc->m_newsymbols.AddSymbol("BU_BO_REL_"); } | T_SG_MUL_VAL { current_dbc->m_newsymbols.AddSymbol("SG_MUL_VAL_"); } ; /************************************************************************/ /* bit_timing_section (BS_) */ /************************************************************************/ bit_timing_section: T_BS T_COLON { ESP_LOGD(TAG,"BS_ parsed empty"); } | T_BS T_COLON T_INT_VAL T_COLON T_INT_VAL T_COMMA T_INT_VAL { ESP_LOGD(TAG,"BS_ parsed %d,%d,%d",(int)$3,(int)$5,(int)$7); current_dbc->m_bittiming.SetBaud($3,$5,$7); } ; /************************************************************************/ /* node_list_section (BU_) */ /************************************************************************/ node_list_section: T_BU T_COLON node_list { ESP_LOGD(TAG,"BU_ parsed %d nodes",current_dbc->m_nodes.GetCount()); } ; node_list: | node_list T_ID { current_dbc->m_nodes.AddNode(new dbcNode($2)); free($2); } ; /************************************************************************/ /* value_table_section (VAL_TABLE_) */ /************************************************************************/ value_table_section: T_VAL_TABLE value_table_list T_SEMICOLON { ESP_LOGD(TAG,"VAL_TABLE_ parsed %s %d values", current_value_table->GetName().c_str(), current_value_table->GetCount()); } ; value_table_list: T_ID T_INT_VAL T_STRING_VAL { current_value_table = new dbcValueTable($1); current_dbc->m_values.AddValueTable($1, current_value_table); current_value_table->AddValue($2, $3); free($1); free($3); } | value_table_list T_INT_VAL T_STRING_VAL { current_value_table->AddValue($2, $3); free($3); } ; /************************************************************************/ /* message_section (BO_) */ /************************************************************************/ /* BO_ 1160 DAS_steeringControl: 4 NEO */ message_section: T_BO T_INT_VAL T_ID T_COLON T_INT_VAL T_ID { ESP_LOGD(TAG,"BO_ parsed message %d",(int)$2); current_message = new dbcMessage((uint32_t)$2); current_message->SetName($3); free($3); current_message->SetSize($5); current_message->SetTransmitterNode($6); free($6); current_dbc->m_messages.AddMessage($2,current_message); } ; /************************************************************************/ /* signal_section (SG_) */ /************************************************************************/ /* SG_ DAS_steeringAngleRequest : 6|15@0+ (0.1,-1638.35) [-1638.35|1638.35] "deg" EPAS */ signal_section: T_SG T_ID signal_mux T_COLON signal_start T_SEP signal_length T_AT signal_endian signal_sign T_PAR_OPEN signal_scale T_COMMA signal_offset T_PAR_CLOSE T_BOX_OPEN signal_min T_SEP signal_max T_BOX_CLOSE T_STRING_VAL receiver_list { ESP_LOGD(TAG,"SG_ parsed signal %s",$2); current_signal->SetName($2); free($2); if (current_message == NULL) { yyerror(current_dbc, "SG_ not after a BO_ message"); free($21); current_signal = NULL; YYABORT; } if ($3 == NULL) { current_signal->ClearMultiplexed(); } else { switch($3[0]) { case 'M': current_message->SetMultiplexorSignal(current_signal); break; case 'm': current_signal->SetMultiplexed((uint32_t)strtoul($3+1, NULL, 10)); break; default: /* error: unknown mux type */ break; } free($3); } current_signal->SetStartSize($5,$7); current_signal->SetByteOrder((dbcByteOrder_t)$9); current_signal->SetValueType(($10 == 0)?DBC_VALUETYPE_UNSIGNED:DBC_VALUETYPE_SIGNED); current_signal->SetFactorOffset($12,$14); current_signal->SetMinMax($17,$19); current_signal->SetUnit($21); free($21); current_message->AddSignal(current_signal); current_signal = NULL; } ; double_val: T_DOUBLE_VAL { $$ = $1; } | T_NAN { $$ = NAN; } | T_INT_VAL { $$ = (double)$1; } ; signal_mux: { $$ = NULL; } | T_ID { $$ = $1; } ; signal_endian: T_INT_VAL { $$ = $1; } ; signal_sign: T_PLUS { $$ = 0; } | T_MINUS { $$ = 1; } ; signal_start: T_INT_VAL { $$ = $1; }; signal_length: T_INT_VAL { $$ = $1; }; signal_scale: double_val { $$ = $1; }; signal_offset: double_val { $$ = $1; }; signal_min: double_val { $$ = $1; }; signal_max: double_val { $$ = $1; }; receiver_list: receiver | receiver_list T_COMMA receiver ; receiver: T_ID { if (current_signal == NULL) current_signal = new dbcSignal(); current_signal->AddReceiver(std::string($1)); free($1); } ; /************************************************************************/ /* value_section (VAL_) */ /************************************************************************/ /* VAL_ 792 GTW_updateInProgress 1 "IN_PROGRESS" 2 "IN_PROGRESS_NOT_USED"; */ value_section: value_list T_SEMICOLON ; value_list: | T_VAL T_INT_VAL T_ID T_INT_VAL T_STRING_VAL { dbcMessage* m = current_dbc->m_messages.FindMessage((uint32_t)$2); if (m == NULL) { yyerror(current_dbc, "VAL_ message not found"); free($3); free($5); YYABORT; } current_signal = m->FindSignal(std::string($3)); if (current_signal == NULL) { yyerror(current_dbc, "VAL_ signal not found (in message)"); free($3); free($5); YYABORT; } ESP_LOGD(TAG,"VAL_ parsed %d/%s",(int)$2,$3); current_signal->AddValue((uint32_t)$4, std::string($5)); free($3); free($5); } | value_list T_INT_VAL T_STRING_VAL { current_signal->AddValue((uint32_t)$2, std::string($3)); free($3); } ; /************************************************************************/ /* attribute_section_list (BA_DEF_) */ /************************************************************************/ /* BA_DEF_ BO_ "GenMsgBackgroundColor" STRING ; */ attribute_section: T_BA_DEF attribute_object_type T_STRING_VAL T_STRING T_SEMICOLON | T_BA_DEF attribute_object_type T_STRING_VAL T_INT T_INT_VAL T_INT_VAL T_SEMICOLON attribute_object_type: T_BU | T_BO | T_SG | T_EV /************************************************************************/ /* attribute_default_section_list (BA_DEF_DEF_) */ /************************************************************************/ /* BA_DEF_DEF_ "GenMsgBackgroundColor" "#1e1e1e"; */ attribute_default_section: T_BA_DEF_DEF T_STRING_VAL T_STRING_VAL T_SEMICOLON | T_BA_DEF_DEF T_STRING_VAL T_INT_VAL T_SEMICOLON /************************************************************************/ /* comment_section_list (CM_) */ /************************************************************************/ /* CM_ "This is my comment"; */ comment_section: T_CM T_STRING_VAL T_SEMICOLON { ESP_LOGD(TAG,"CM_ parsed %s",$2); current_dbc->m_comments.AddComment($2); free($2); } | T_CM T_BU T_ID T_STRING_VAL T_SEMICOLON { dbcNode* n = current_dbc->m_nodes.FindNode(std::string($3)); if (n != NULL) { ESP_LOGD(TAG,"CM_ BU_ parsed %s",$3); n->AddComment($4); } else { yyerror(current_dbc, "BU_ node not found"); free($3); free($4); YYABORT; } free($3); free($4); } | T_CM T_BO T_INT_VAL T_STRING_VAL T_SEMICOLON { dbcMessage* m = current_dbc->m_messages.FindMessage((uint32_t)$3); if (m != NULL) { ESP_LOGD(TAG,"CM_ BO_ parsed %s",$4); m->AddComment($4); } else { yyerror(current_dbc, "BO_ message not found"); free($4); YYABORT; } free($4); } | T_CM T_SG T_INT_VAL T_ID T_STRING_VAL T_SEMICOLON { dbcMessage* m = current_dbc->m_messages.FindMessage((uint32_t)$3); if (m != NULL) { dbcSignal* s = m->FindSignal(std::string($4)); if (s != NULL) { ESP_LOGD(TAG,"CM_ SG_ parsed %s",$5); s->AddComment($5); } else { yyerror(current_dbc, "BO_ signal not found (in message)"); free($4); free($5); YYABORT; } } else { yyerror(current_dbc, "SG_ message not found"); free($4); free($5); YYABORT; } free($4); free($5); } | T_CM T_EV T_ID T_STRING_VAL T_SEMICOLON { yyerror(current_dbc, "EV_ not currently supported"); free($3); free($4); YYABORT; } ;