Skip to content

Commit

Permalink
Improve DynamicSelect handling in OMEdit (#7952)
Browse files Browse the repository at this point in the history
- Improve StringHandler::getStrings to handle strings that contain
  Modelica expressions better.
- Change both old and new frontend to just return DynamicSelect calls as
  they are and let OMEdit handle them.
  • Loading branch information
perost committed Sep 29, 2021
1 parent 51bb7b1 commit d6d4eaf
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 141 deletions.
36 changes: 6 additions & 30 deletions OMCompiler/Compiler/FrontEnd/Static.mo
Expand Up @@ -3836,9 +3836,7 @@ algorithm
end getHomotopyArguments;

protected function elabBuiltinDynamicSelect
"Elaborates DynamicSelect statements in annotations for OMEdit.
Currently only text annotations with one String statement accessing
one variable are supported. Otherwise the first argument is returned."
"Elaborates DynamicSelect statements in annotations for OMEdit."
input FCore.Cache inCache;
input FCore.Graph inEnv;
input list<Absyn.Exp> inPosArgs;
Expand All @@ -3859,37 +3857,15 @@ algorithm
msg_str := ", expected DynamicSelect(staticExp, dynamicExp)";
printBuiltinFnArgError("DynamicSelect", msg_str, inPosArgs, inNamedArgs, inPrefix, inInfo);
end if;

{astatic, adynamic} := inPosArgs;
(outCache, dstatic, outProperties as DAE.PROP(ty, _)) :=
elabExpInExpression(inCache, inEnv, astatic, inImplicit, true, inPrefix, inInfo);

try
outExp := match (astatic, adynamic)
local
Absyn.ComponentRef acref;
Integer digits;
list<Absyn.NamedArg> namedArgs;
Boolean bconst;
// keep DynamicSelect for String with cref arg (textString)
case (Absyn.STRING(), Absyn.CALL(function_ = Absyn.CREF_IDENT(name = "String"), functionArgs = Absyn.FUNCTIONARGS(args = {Absyn.CREF(componentRef = acref)}, argNames = namedArgs))) algorithm
(outCache, dstatic, outProperties as DAE.PROP(ty, _)) :=
elabExpInExpression(inCache, inEnv, astatic, inImplicit, true, inPrefix, inInfo);
ddynamic := Expression.crefToExp(absynCrefToComponentReference(acref));
// Note: can't generate Modelica syntax as OMEdit only parses lists
//outExp := Expression.makePureBuiltinCall("DynamicSelect", {dstatic,
// Expression.makePureBuiltinCall("String", {ddynamic}, ty)}, ty);
outExp := match namedArgs
case {Absyn.NAMEDARG(argName = "significantDigits", argValue = Absyn.INTEGER(value = digits))}
then Expression.makeArray({dstatic, ddynamic, DAE.ICONST(digits)}, ty, true);
case {Absyn.NAMEDARG(), Absyn.NAMEDARG(argName = "significantDigits", argValue = Absyn.INTEGER(value = digits))}
then Expression.makeArray({dstatic, ddynamic, DAE.ICONST(digits)}, ty, true);
else Expression.makeArray({dstatic, ddynamic}, ty, true);
end match;
then outExp;
// keep DynamicSelect for Boolean with cref arg (visible, primitivesVisible)
case (Absyn.BOOL(), Absyn.CREF(componentRef = acref))
then Expression.makeArray({dstatic, Expression.crefToExp(absynCrefToComponentReference(acref))}, ty, true);
end match;
// return first argument of DynamicSelect for model editing per default
(outCache, ddynamic, _) :=
elabExpInExpression(outCache, inEnv, adynamic, inImplicit, true, inPrefix, inInfo);
outExp := Expression.makePureBuiltinCall("DynamicSelect", {dstatic, ddynamic}, ty);
else
outExp := dstatic;
end try;
Expand Down
38 changes: 0 additions & 38 deletions OMCompiler/Compiler/NFFrontEnd/NFSimplifyExp.mo
Expand Up @@ -232,7 +232,6 @@ algorithm
then
exp;

case "DynamicSelect" then simplifyDynamicSelect(args, call);
case "fill" then simplifyFill(listHead(args), listRest(args), call);
case "homotopy" then simplifyHomotopy(args, call);
case "max" guard listLength(args) == 1 then simplifyReducedArrayConstructor(listHead(args), call);
Expand Down Expand Up @@ -373,43 +372,6 @@ algorithm
end match;
end simplifyHomotopy;

function simplifyDynamicSelect
input list<Expression> args;
input Call call;
output Expression exp;
protected
list<Expression> str_args;
Expression dstatic, ddynamic, var, digits;
Function fn;
algorithm
exp := Expression.STRING(Expression.toString(Expression.CALL(call)));
//if Flags.isSet(Flags.NF_API_DYNAMIC_SELECT) then
// exp := Expression.CALL(call);
// return;
//end if;

//{dstatic, ddynamic} := args;

//// HACK, TODO, FIXME! handle DynamicSelect properly in OMEdit, then disable this stuff!
//exp := match (dstatic, ddynamic)
// // DynamicSelect("%y", String(y, significantDigits = 3)) => {"%y", y, 3}
// case (Expression.STRING(), Expression.CALL(call = Call.TYPED_CALL(
// fn = Function.FUNCTION(path = Absyn.Path.IDENT("String")), arguments = str_args)))
// guard listLength(str_args) == 4
// algorithm
// var :: digits :: _ := str_args;
// then
// Expression.makeArray(Type.UNKNOWN(), {dstatic, var, digits});

// // DynamicSelect(true, y) => {true, y}
// case (Expression.BOOLEAN(), Expression.CREF())
// then Expression.makeArray(Type.UNKNOWN(), args);

// // Otherwise just return first argument
// else dstatic;
//end match;
end simplifyDynamicSelect;

function simplifyArrayConstructor
input Call call;
output Expression outExp;
Expand Down
2 changes: 1 addition & 1 deletion OMEdit/OMEditLIB/Element/Transformation.cpp
Expand Up @@ -92,7 +92,7 @@ void Transformation::parseTransformationString(QString value, qreal width, qreal
if (value1.isEmpty()) {
return;
}
QStringList annotations = StringHandler::getStrings(value1, '(', ')');
QStringList annotations = StringHandler::getStrings(value1);
foreach (QString annotation, annotations) {
if (annotation.startsWith("Placement")) {
annotation = annotation.mid(QString("Placement").length());
Expand Down
14 changes: 7 additions & 7 deletions OMEdit/OMEditLIB/Modeling/ModelWidgetContainer.cpp
Expand Up @@ -4846,7 +4846,7 @@ void ModelWidget::addConnection(QStringList connectionList, QString connectionAn
return;
}
// connection annotation
QStringList shapesList = StringHandler::getStrings(connectionAnnotationString, '(', ')');
QStringList shapesList = StringHandler::getStrings(connectionAnnotationString);
// Now parse the shapes available in list
QString lineShape = "";
foreach (QString shape, shapesList) {
Expand Down Expand Up @@ -6700,7 +6700,7 @@ void ModelWidget::getModelIconDiagramShapes(StringHandler::ViewType viewType)
// read the shapes
if (list.size() < 9)
return;
QStringList shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(list.at(8)), '(', ')');
QStringList shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(list.at(8)));
drawModelIconDiagramShapes(shapesList, pGraphicsView, false);
}

Expand Down Expand Up @@ -6929,7 +6929,7 @@ void ModelWidget::getModelTransitions()
continue;
}
// get the transition annotations
QStringList shapesList = StringHandler::getStrings(transition.at(7), '(', ')');
QStringList shapesList = StringHandler::getStrings(transition.at(7));
// Now parse the shapes available in list
QString lineShape, textShape = "";
foreach (QString shape, shapesList) {
Expand Down Expand Up @@ -6970,7 +6970,7 @@ void ModelWidget::getModelInitialStates()
continue;
}
// get the transition annotations
QStringList shapesList = StringHandler::getStrings(initialState.at(1), '(', ')');
QStringList shapesList = StringHandler::getStrings(initialState.at(1));
// Now parse the shapes available in list
QString lineShape = "";
foreach (QString shape, shapesList) {
Expand Down Expand Up @@ -7154,7 +7154,7 @@ void ModelWidget::getCompositeModelConnections()
QDomElement annotationElement = connectionChildren.at(j).toElement();
if (annotationElement.tagName().compare("Annotation") == 0) {
annotationFound = true;
shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(QString(annotation).arg(annotationElement.attribute("Points"))), '(', ')');
shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(QString(annotation).arg(annotationElement.attribute("Points"))));
}
}
if (!annotationFound) {
Expand All @@ -7165,7 +7165,7 @@ void ModelWidget::getCompositeModelConnections()
QPointF endPoint = pEndInterfacePointComponent->mapToScene(pEndInterfacePointComponent->boundingRect().center());
points.append(point.arg(endPoint.x()).arg(endPoint.y()));
QString pointsString = QString("{%1}").arg(points.join(","));
shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(QString(annotation).arg(pointsString)), '(', ')');
shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(QString(annotation).arg(pointsString)));
}
// Now parse the shapes available in list
QString lineShape = "";
Expand Down Expand Up @@ -7402,7 +7402,7 @@ void ModelWidget::drawOMSModelConnections()
QPointF endPoint = mpDiagramGraphicsView->roundPoint(pEndConnectorComponent->mapToScene(pEndConnectorComponent->boundingRect().center()));
points.append(point.arg(endPoint.x()).arg(endPoint.y()));
QString pointsString = QString("{%1}").arg(points.join(","));
shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(QString(annotation).arg(pointsString)), '(', ')');
shapesList = StringHandler::getStrings(StringHandler::removeFirstLastCurlBrackets(QString(annotation).arg(pointsString)));
// Now parse the shapes available in list
QString lineShape = "";
foreach (QString shape, shapesList) {
Expand Down
117 changes: 58 additions & 59 deletions OMEdit/OMEditLIB/Util/StringHandler.cpp
Expand Up @@ -886,73 +886,72 @@ QString StringHandler::removeFirstLastSingleQuotes(QString value)
return value;
}

/*!
* \brief StringHandler::getStrings
* Splits a comma-separated string into a list of strings while preserving
* Modelica expressions such as strings, arrays and records.
* \param value
* \return
*/
QStringList StringHandler::getStrings(QString value)
{
return getStrings(value, '{', '}');
}
QStringList res;
int blocks = 0;
bool escaped = false;
char str_char = 0;
int start = 0;
int n = 0;

QStringList StringHandler::getStrings(QString value, char start, char end)
{
QStringList list;
bool mask = false;
bool inString = false;
char StringEnd = '\0';
int begin = 0;
int ele = 0;
for (auto &c: value) {
++n;

for (int i = 0 ; i < value.length() ; i++)
{
if (inString)
{
if (mask)
{
mask = false;
}
else
{
if (value.at(i) == '\\')
{
mask = true;
}
else if (value.at(i) == StringEnd)
{
inString = false;
}
if (escaped) {
escaped = false;
continue;
} else if (str_char) {
if (c == str_char) {
str_char = 0;
}
continue;
}
else
{
if (value.at(i) == '"')
{
StringEnd = '"';
inString = true;
}
else if (value.at(i) == '\'')
{
StringEnd = '\'';
inString = true;
}
else if (value.at(i) == ',')
{
if (ele == 0)
{
list.append(value.mid(begin,i-begin).trimmed());
begin = i+1;

switch (c.unicode()) {
case '{':
case '(':
case '[':
++blocks;
break;

case '}':
case ')':
case ']':
--blocks;
break;

case '\\':
escaped = true;
break;

case '"':
case '\'':
str_char = c.unicode();
break;

case ',':
if (blocks == 0) {
res.append(value.mid(start, n-1).trimmed());
start += n;
n = 0;
}
}
else if (value.at(i) == start)
{
ele++;
}
else if (value.at(i) == end)
{
ele--;
}
break;
}
}
list.append(value.mid(begin,value.length()-begin).trimmed());

return list;
if (n > 0) {
res.append(value.mid(start).trimmed());
}

return res;
}

/*!
Expand Down Expand Up @@ -1410,7 +1409,7 @@ QStringList StringHandler::getAnnotation(QString componentAnnotation, QString an
if (componentAnnotation.isEmpty()) {
return QStringList();
}
QStringList annotations = StringHandler::getStrings(componentAnnotation, '(', ')');
QStringList annotations = StringHandler::getStrings(componentAnnotation);
foreach (QString annotation, annotations) {
if (annotation.startsWith(annotationName)) {
annotation = annotation.mid(QString(annotationName).length());
Expand All @@ -1431,7 +1430,7 @@ QString StringHandler::getPlacementAnnotation(QString componentAnnotation)
if (componentAnnotation.isEmpty()) {
return "";
}
QStringList annotations = StringHandler::getStrings(componentAnnotation, '(', ')');
QStringList annotations = StringHandler::getStrings(componentAnnotation);
foreach (QString annotation, annotations) {
if (annotation.startsWith("Placement")) {
QString placementAnnotation = StringHandler::removeFirstLastParentheses(annotation);
Expand Down
1 change: 0 additions & 1 deletion OMEdit/OMEditLIB/Util/StringHandler.h
Expand Up @@ -116,7 +116,6 @@ class StringHandler : public QObject
static QString removeFirstLastQuotes(QString value);
static QString removeFirstLastSingleQuotes(QString value);
static QStringList getStrings(QString value);
static QStringList getStrings(QString value, char start, char end);
/* Handles quoted identifiers A.B.'C.D' -> A.B, A.B.C.D -> A.B.C */
static QString getLastWordAfterDot(QString value);
static QString removeLastWordAfterDot(QString value);
Expand Down
4 changes: 2 additions & 2 deletions testsuite/openmodelica/interactive-API/Ticket6167.mos
Expand Up @@ -17,8 +17,8 @@ getIconAnnotation(Modelica.Fluid.Vessels.OpenTank); getErrorString();
// ""
// true
// ""
// {-100.0,-100.0,100.0,100.0,true,-,-,, {Line(true, {0.0, 0.0}, 0.0, {{0.0, 50.0}, {0.0, 0.0}}, {0, 0, 0}, LinePattern.Solid, 0.25, {Arrow.None, Arrow.None}, 3.0, Smooth.None), Rectangle(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.Solid, 0.25, BorderPattern.None, {{-20.0, 60.0}, {20.0, 50.0}}, 0.0), Polygon(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {255, 255, 255}, LinePattern.Solid, FillPattern.Solid, 0.25, {{-100.0, 50.0}, {100.0, -50.0}, {100.0, 50.0}, {0.0, 0.0}, {-100.0, -50.0}, {-100.0, 50.0}}, Smooth.None), Polygon(true, {0.0, 0.0}, 0.0, {255, 255, 255}, {0, 255, 0}, LinePattern.Solid, FillPattern.Solid, 0.25, "DynamicSelect({{-100, 0}, {100, 0}, {100, 0}, {0, 0}, {-100, 0}, {-100, 0}}, {{-100.0, 50.0 * opening}, {-100.0, 50.0 * opening}, {100.0, -50.0 * opening}, {100.0, 50.0 * opening}, {0.0, 0.0}, {-100.0, -50.0 * opening}, {-100.0, 50.0 * opening}})", Smooth.None), Polygon(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-100.0, 50.0}, {100.0, -50.0}, {100.0, 50.0}, {0.0, 0.0}, {-100.0, -50.0}, {-100.0, 50.0}}, Smooth.None)}}
// {-100.0,-100.0,100.0,100.0,true,-,-,, {Line(true, {0.0, 0.0}, 0.0, {{0.0, 50.0}, {0.0, 0.0}}, {0, 0, 0}, LinePattern.Solid, 0.25, {Arrow.None, Arrow.None}, 3.0, Smooth.None), Rectangle(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.Solid, 0.25, BorderPattern.None, {{-20.0, 60.0}, {20.0, 50.0}}, 0.0), Polygon(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {255, 255, 255}, LinePattern.Solid, FillPattern.Solid, 0.25, {{-100.0, 50.0}, {100.0, -50.0}, {100.0, 50.0}, {0.0, 0.0}, {-100.0, -50.0}, {-100.0, 50.0}}, Smooth.None), Polygon(true, {0.0, 0.0}, 0.0, {255, 255, 255}, {0, 255, 0}, LinePattern.Solid, FillPattern.Solid, 0.25, DynamicSelect({{-100, 0}, {100, 0}, {100, 0}, {0, 0}, {-100, 0}, {-100, 0}}, {{-100.0, 50.0 * opening}, {-100.0, 50.0 * opening}, {100.0, -50.0 * opening}, {100.0, 50.0 * opening}, {0.0, 0.0}, {-100.0, -50.0 * opening}, {-100.0, 50.0 * opening}}), Smooth.None), Polygon(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-100.0, 50.0}, {100.0, -50.0}, {100.0, 50.0}, {0.0, 0.0}, {-100.0, -50.0}, {-100.0, 50.0}}, Smooth.None)}}
// ""
// {-100.0,-100.0,100.0,100.0,true,0.2,-,, {Rectangle(true, {0.0, 0.0}, 0.0, {255, 255, 255}, {255, 255, 255}, LinePattern.Solid, FillPattern.VerticalCylinder, 0.25, BorderPattern.None, {{-100.0, 100.0}, {100.0, -100.0}}, 0.0), Rectangle(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {85, 170, 255}, LinePattern.Solid, FillPattern.VerticalCylinder, 0.25, BorderPattern.None, "DynamicSelect({{-100, -100}, {100, 10}}, {{-100.0, -100.0}, {100.0, (-100.0) + 200.0 * level / height}})", 0.0), Line(true, {0.0, 0.0}, 0.0, {{-100.0, 100.0}, {-100.0, -100.0}, {100.0, -100.0}, {100.0, 100.0}}, {0, 0, 0}, LinePattern.Solid, 0.25, {Arrow.None, Arrow.None}, 3.0, Smooth.None), Text(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-95.0, 60.0}, {95.0, 40.0}}, "level =", 0.0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-95.0, -24.0}, {95.0, -44.0}}, "DynamicSelect("%level_start", String(level, 2, 1, true))", 0.0, {-1, -1, -1}, "", {}, TextAlignment.Center)}}
// {-100.0,-100.0,100.0,100.0,true,0.2,-,, {Rectangle(true, {0.0, 0.0}, 0.0, {255, 255, 255}, {255, 255, 255}, LinePattern.Solid, FillPattern.VerticalCylinder, 0.25, BorderPattern.None, {{-100.0, 100.0}, {100.0, -100.0}}, 0.0), Rectangle(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {85, 170, 255}, LinePattern.Solid, FillPattern.VerticalCylinder, 0.25, BorderPattern.None, DynamicSelect({{-100, -100}, {100, 10}}, {{-100.0, -100.0}, {100.0, (-100.0) + 200.0 * level / height}}), 0.0), Line(true, {0.0, 0.0}, 0.0, {{-100.0, 100.0}, {-100.0, -100.0}, {100.0, -100.0}, {100.0, 100.0}}, {0, 0, 0}, LinePattern.Solid, 0.25, {Arrow.None, Arrow.None}, 3.0, Smooth.None), Text(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-95.0, 60.0}, {95.0, 40.0}}, "level =", 0.0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text(true, {0.0, 0.0}, 0.0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-95.0, -24.0}, {95.0, -44.0}}, DynamicSelect("%level_start", String(level, 2, 1, true)), 0.0, {-1, -1, -1}, "", {}, TextAlignment.Center)}}
// ""
// endResult
Expand Up @@ -41,6 +41,6 @@ getIconAnnotation(IconWithValues.Component); getErrorString();
// ""
// ""
// ""
// {-,-,-,-,-,0.1,-,,{Rectangle({false, neg}, {0, 0}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, BorderPattern.None, {{-95, 95}, {95, -95}}, 0), Text(true, {-55, 35}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-35, 15}, {50, -30}}, "x = ", 0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text(true, {-55, 35}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{50, 15}, {150, -30}}, {"x", x}, 0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text({true, pos}, {-55, -30}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-35, 15}, {150, -30}}, {"%y", y, 3}, 0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text({false, neg}, {-55, -30}, 0, {255, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-35, 15}, {150, -30}}, {"%y", y, 3}, 0, {-1, -1, -1}, "", {}, TextAlignment.Center)}}
// {-,-,-,-,-,0.1,-,,{Rectangle(DynamicSelect(false, neg), {0, 0}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, BorderPattern.None, {{-95, 95}, {95, -95}}, 0), Text(true, {-55, 35}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-35, 15}, {50, -30}}, "x = ", 0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text(true, {-55, 35}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{50, 15}, {150, -30}}, DynamicSelect("x", String(x, 6, 0, true)), 0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text(DynamicSelect(true, pos), {-55, -30}, 0, {0, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-35, 15}, {150, -30}}, DynamicSelect("%y", String(y, 3, 0, true)), 0, {-1, -1, -1}, "", {}, TextAlignment.Center), Text(DynamicSelect(false, neg), {-55, -30}, 0, {255, 0, 0}, {0, 0, 0}, LinePattern.Solid, FillPattern.None, 0.25, {{-35, 15}, {150, -30}}, DynamicSelect("%y", String(y, 3, 0, true)), 0, {-1, -1, -1}, "", {}, TextAlignment.Center)}}
// ""
// endResult

0 comments on commit d6d4eaf

Please sign in to comment.