Skip to content

Commit

Permalink
Validate structured variable names (#416)
Browse files Browse the repository at this point in the history
fixes #409
  • Loading branch information
t-sommer authored Oct 18, 2023
1 parent fb56551 commit 6792b71
Show file tree
Hide file tree
Showing 10 changed files with 3,618 additions and 1 deletion.
9 changes: 8 additions & 1 deletion fmusim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ set(sources
miniunzip.c
${ZLIB_SRC_DIR}/contrib/minizip/ioapi.c
${ZLIB_SRC_DIR}/contrib/minizip/unzip.c
../src/structured_variable_name.yy.c
../include/structured_variable_name.tab.h
../src/structured_variable_name.tab.c
)

if (WIN32)
Expand All @@ -82,7 +85,11 @@ target_include_directories(fmusim PRIVATE
${CVODE_DIR}/include
)

add_compile_definitions(LIBXML_STATIC)
if (WIN32)
target_compile_definitions(fmusim PRIVATE YY_NO_UNISTD_H LIBXML_STATIC)
else ()
target_compile_definitions(fmusim PRIVATE LIBXML_STATIC)
endif ()

if (WIN32)
set(libraries
Expand Down
50 changes: 50 additions & 0 deletions fmusim/FMIModelDescription.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
#include <Windows.h>
#endif

#include "structured_variable_name.tab.h"

#include "fmi1schema.h"
#include "fmi2schema.h"
#include "fmi3schema.h"


static bool getBooleanAttribute(const xmlNodePtr node, const char* name) {
char* literal = (char*)xmlGetProp(node, (xmlChar*)name);
bool value = literal && (strcmp(literal, "true") == 0 || strcmp(literal, "1") == 0);
Expand All @@ -30,6 +33,13 @@ static uint32_t getUInt32Attribute(const xmlNodePtr node, const char* name) {
return value;
}

static FMIVariableNamingConvention getVariableNamingConvention(const xmlNodePtr node) {
const char* value = (char*)xmlGetProp(node, (xmlChar*)"variableNamingConvention");
FMIVariableNamingConvention variableNamingConvention = (value && !strcmp(value, "structured")) ? FMIStructured : FMIFlat;
free(value);
return variableNamingConvention;
}

static FMIModelDescription* readModelDescriptionFMI1(xmlNodePtr root) {

FMIModelDescription* modelDescription = (FMIModelDescription*)calloc(1, sizeof(FMIModelDescription));
Expand All @@ -44,6 +54,7 @@ static FMIModelDescription* readModelDescriptionFMI1(xmlNodePtr root) {
modelDescription->description = (char*)xmlGetProp(root, (xmlChar*)"description");
modelDescription->generationTool = (char*)xmlGetProp(root, (xmlChar*)"generationTool");
modelDescription->generationDate = (char*)xmlGetProp(root, (xmlChar*)"generationDateAndTime");
modelDescription->variableNamingConvention = getVariableNamingConvention(root);

const char* numberOfContinuousStates = (char*)xmlGetProp(root, (xmlChar*)"numberOfContinuousStates");

Expand Down Expand Up @@ -175,6 +186,8 @@ static FMIModelDescription* readModelDescriptionFMI1(xmlNodePtr root) {

xmlXPathFreeContext(xpathCtx);

nProblems += FMIValidateVariableNames(modelDescription);

if (nProblems > 0) {
FMIFreeModelDescription(modelDescription);
modelDescription = NULL;
Expand Down Expand Up @@ -245,6 +258,7 @@ static FMIModelDescription* readModelDescriptionFMI2(xmlNodePtr root) {
modelDescription->description = (char*)xmlGetProp(root, (xmlChar*)"description");
modelDescription->generationTool = (char*)xmlGetProp(root, (xmlChar*)"generationTool");
modelDescription->generationDate = (char*)xmlGetProp(root, (xmlChar*)"generationDate");
modelDescription->variableNamingConvention = getVariableNamingConvention(root);

const char* numberOfEventIndicators = (char*)xmlGetProp(root, (xmlChar*)"numberOfEventIndicators");

Expand Down Expand Up @@ -389,6 +403,8 @@ static FMIModelDescription* readModelDescriptionFMI2(xmlNodePtr root) {

nProblems += FMIValidateModelStructure(modelDescription);

nProblems += FMIValidateVariableNames(modelDescription);

if (nProblems > 0) {
FMIFreeModelDescription(modelDescription);
modelDescription = NULL;
Expand All @@ -411,6 +427,7 @@ static FMIModelDescription* readModelDescriptionFMI3(xmlNodePtr root) {
modelDescription->description = (char*)xmlGetProp(root, (xmlChar*)"description");
modelDescription->generationTool = (char*)xmlGetProp(root, (xmlChar*)"generationTool");
modelDescription->generationDate = (char*)xmlGetProp(root, (xmlChar*)"generationDate");
modelDescription->variableNamingConvention = getVariableNamingConvention(root);

xmlXPathContextPtr xpathCtx = xmlXPathNewContext(root->doc);

Expand Down Expand Up @@ -664,6 +681,8 @@ static FMIModelDescription* readModelDescriptionFMI3(xmlNodePtr root) {

nProblems += FMIValidateModelStructure(modelDescription);

nProblems += FMIValidateVariableNames(modelDescription);

if (nProblems > 0) {
FMIFreeModelDescription(modelDescription);
modelDescription = NULL;
Expand Down Expand Up @@ -875,6 +894,37 @@ size_t FMIValidateModelStructure(const FMIModelDescription* modelDescription) {
return nProblems;
}

/* Declarations */
void set_input_string(const char* in);
void end_lexical_scan(void);

void yyerror(const FMIModelVariable* variable, const char* s) {
printf("\"%s\" (line %d) is not a valid variable name for variableNamingConvention=\"structured\".\n", variable->name, variable->line);
}

size_t FMIValidateVariableNames(const FMIModelDescription* modelDescription) {

size_t nProblems = 0;

if (modelDescription->variableNamingConvention == FMIStructured) {

for (size_t i = 0; i < modelDescription->nModelVariables; i++) {

const FMIModelVariable* variable = &modelDescription->modelVariables[i];

set_input_string(variable->name);

if (yyparse(variable)) {
nProblems++;
}

end_lexical_scan();
}
}

return nProblems;
}

void FMIDumpModelDescription(FMIModelDescription* modelDescription, FILE* file) {

fprintf(file, "FMI Version 3.0\n");
Expand Down
10 changes: 10 additions & 0 deletions fmusim/FMIModelDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ typedef enum {

} FMIVariability;

typedef enum {

FMIFlat,
FMIStructured

} FMIVariableNamingConvention;

typedef struct FMIDimension FMIDimension;

typedef struct FMIModelVariable FMIModelVariable;
Expand Down Expand Up @@ -90,6 +97,7 @@ typedef struct {
const char* description;
const char* generationTool;
const char* generationDate;
FMIVariableNamingConvention variableNamingConvention;

FMIModelExchangeInterface* modelExchange;
FMICoSimulationInterface* coSimulation;
Expand Down Expand Up @@ -127,4 +135,6 @@ FMIModelVariable* FMIModelVariableForIndexLiteral(const FMIModelDescription* mod

size_t FMIValidateModelStructure(const FMIModelDescription* modelDescription);

size_t FMIValidateVariableNames(const FMIModelDescription* modelDescription);

void FMIDumpModelDescription(FMIModelDescription* modelDescription, FILE* file);
71 changes: 71 additions & 0 deletions include/structured_variable_name.tab.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* A Bison parser, made by GNU Bison 3.5.1. */

/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */

/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */

/* Undocumented macros, especially those whose name start with YY_,
are private implementation details. Do not rely on them. */

#ifndef YY_YY_STRUCTURED_VARIABLE_NAME_TAB_H_INCLUDED
# define YY_YY_STRUCTURED_VARIABLE_NAME_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
#endif
#if YYDEBUG
extern int yydebug;
#endif

/* Token type. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
DER = 258,
UNSIGNED_INTEGER = 259,
NONDIGIT = 260,
Q_NAME = 261
};
#endif

/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef int YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif


extern YYSTYPE yylval;

int yyparse (void* variable);

#endif /* !YY_YY_STRUCTURED_VARIABLE_NAME_TAB_H_INCLUDED */
3 changes: 3 additions & 0 deletions lint_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def fix_file(filename):
if not file.lower().endswith(('.h', '.c', '.md', '.html', '.csv', '.txt', '.xml')):
continue

if file.lower().endswith(('.tab.c', '.yy.c')):
continue # generated files

filename = os.path.join(root, file)

print(filename)
Expand Down
7 changes: 7 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Regenerate parser for structured variables names

- `sudo apt install flex bison`
- `cd src`
- `bison -d structured_variable_name.y`
- `flex --outfile=structured_variable_name.yy.c structured_variable_name.l`
- `mv structured_variable_name.tab.h ../include/`
41 changes: 41 additions & 0 deletions src/structured_variable_name.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
%option noyywrap

%{
#include <stdio.h>

#define YY_DECL int yylex()

#include "structured_variable_name.tab.h"

%}

q_name "'"({q_char}|{s_escape})+"'"
nondigit [_a-zA-Z]
digit [0-9]
q_char {nondigit}|{digit}|[!#$%&()*+,-\./:;<>=?@\[\]\^{}|~ ]
s_escape ("\\'")|("\\\"")|("\\?")|("\\\\")|("\\a")|("\\b")|("\\f")|("\\n")|("\\r")|("\\t")|("\\v")
unsigned_integer {digit}+

%%

"der(" { return DER; }
"(" { return '('; }
")" { return ')'; }
"," { return ','; }
"." { return '.'; }
"[" { return '['; }
"]" { return ']'; }
{q_name} { return Q_NAME; }
{nondigit} { return NONDIGIT; }
{unsigned_integer} { return UNSIGNED_INTEGER; }
. { return *yytext; }

%%

void set_input_string(const char* in) {
yy_scan_string(in);
}

void end_lexical_scan(void) {
yy_delete_buffer(YY_CURRENT_BUFFER);
}
Loading

0 comments on commit 6792b71

Please sign in to comment.