From d4be4235e707ab737b88163878f08d086f33300a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20=C3=96stlund?= Date: Tue, 19 Mar 2024 13:35:26 +0100 Subject: [PATCH] Improve copyClass API (#12120) - Improved `copyClass` so that it updates the paths in the copied class to be valid in the new location of the class where possible. - Changed default `within` argument of `copyClass` from `TopLevel` to `__OpenModelica_TopLevel` to make it a bit less likely to conflict with user models. --- .../Compiler/FrontEnd/ModelicaBuiltin.mo | 2 +- OMCompiler/Compiler/NFFrontEnd/NFLookup.mo | 68 +++ .../Compiler/NFFrontEnd/NFModelicaBuiltin.mo | 2 +- .../Compiler/Script/CevalScriptBackend.mo | 8 +- OMCompiler/Compiler/Script/NFApi.mo | 505 ++++++++++++++++++ OMEdit/OMEditLIB/OMC/OMCProxy.cpp | 2 +- .../interactive-API/CopyClass.mos | 2 +- .../interactive-API/CopyClass1.mos | 40 ++ .../interactive-API/CopyClass2.mos | 73 +++ .../interactive-API/CopyClass3.mos | 37 ++ .../interactive-API/CopyClass4.mos | 35 ++ .../interactive-API/CopyClass5.mos | 34 ++ .../interactive-API/CopyClassInvalid1.mos | 34 ++ .../interactive-API/CopyClassRedeclare1.mos | 40 ++ .../openmodelica/interactive-API/Makefile | 7 + 15 files changed, 883 insertions(+), 6 deletions(-) create mode 100644 testsuite/openmodelica/interactive-API/CopyClass1.mos create mode 100644 testsuite/openmodelica/interactive-API/CopyClass2.mos create mode 100644 testsuite/openmodelica/interactive-API/CopyClass3.mos create mode 100644 testsuite/openmodelica/interactive-API/CopyClass4.mos create mode 100644 testsuite/openmodelica/interactive-API/CopyClass5.mos create mode 100644 testsuite/openmodelica/interactive-API/CopyClassInvalid1.mos create mode 100644 testsuite/openmodelica/interactive-API/CopyClassRedeclare1.mos diff --git a/OMCompiler/Compiler/FrontEnd/ModelicaBuiltin.mo b/OMCompiler/Compiler/FrontEnd/ModelicaBuiltin.mo index 3572f338722..10dd6ca6d15 100644 --- a/OMCompiler/Compiler/FrontEnd/ModelicaBuiltin.mo +++ b/OMCompiler/Compiler/FrontEnd/ModelicaBuiltin.mo @@ -2714,7 +2714,7 @@ function copyClass "Copies a class within the same level" input TypeName className "the class that should be copied"; input String newClassName "the name for new class"; - input TypeName withIn = $TypeName(TopLevel) "the with in path for new class"; + input TypeName withIn = $TypeName(__OpenModelica_TopLevel) "the with in path for new class"; output Boolean result; external "builtin"; annotation(preferredView="text"); diff --git a/OMCompiler/Compiler/NFFrontEnd/NFLookup.mo b/OMCompiler/Compiler/NFFrontEnd/NFLookup.mo index d0ab99cd257..fe73a89d0f4 100644 --- a/OMCompiler/Compiler/NFFrontEnd/NFLookup.mo +++ b/OMCompiler/Compiler/NFFrontEnd/NFLookup.mo @@ -489,6 +489,74 @@ algorithm fail(); end lookupSimpleName; +function lookupSimpleNameRootPath + "Returns the qualified path needed to find the given name in the given scope + that's either a root class or a class somewhere inside a root class. If the + name is found outside the root class it's fully qualified, otherwise it's + returned as it is." + input String name; + input InstNode scope; + input InstContext.Type context; + output Absyn.Path path; +protected + InstNode node, cur_scope = scope; + Boolean in_root_class = true; +algorithm + if InstContext.inAnnotation(context) then + try + _ := lookupLocalSimpleName(name, InstNode.annotationScope(scope)); + // Name refers to builtin annotation that shouldn't be qualified. + path := Absyn.Path.IDENT(name); + return; + else + end try; + end if; + + for i in 1:Global.recursionDepthLimit loop + try + node := Class.lookupElement(name, InstNode.getClass(cur_scope)); + + if in_root_class then + // If we're still inside the root class, then the name doesn't need to be qualified. + path := Absyn.Path.IDENT(name); + else + // Otherwise, fully qualify the path. + path := AbsynUtil.makeFullyQualified(InstNode.fullPath(node)); + end if; + + return; + else + if InstNode.isEncapsulated(cur_scope) then + // If the scope is encapsulated then the name can only refer to builtin + // classes, which are already fully qualified. + path := Absyn.Path.IDENT(name); + return; + elseif name == InstNode.name(cur_scope) and InstNode.isClass(cur_scope) then + // The current scope is the class we're looking for. + path := if in_root_class then Absyn.Path.IDENT(name) else + AbsynUtil.makeFullyQualified(InstNode.fullPath(cur_scope)); + return; + elseif InstNode.isTopScope(cur_scope) then + // If we reach the top scope then the name is either already fully + // qualified or couldn't be found, in either case return name as it is. + path := Absyn.Path.IDENT(name); + return; + else + if in_root_class and InstNode.isRootClass(cur_scope) then + // The scope was the root class, now we're start to look in the + // enclosing scopes of the root class. + in_root_class := false; + end if; + + // Continue in the enclosing scope. + cur_scope := InstNode.parentScope(cur_scope); + end if; + end try; + end for; + + fail(); +end lookupSimpleNameRootPath; + function lookupNameWithError input Absyn.Path name; input InstNode scope; diff --git a/OMCompiler/Compiler/NFFrontEnd/NFModelicaBuiltin.mo b/OMCompiler/Compiler/NFFrontEnd/NFModelicaBuiltin.mo index d76fd209580..29cea655a04 100644 --- a/OMCompiler/Compiler/NFFrontEnd/NFModelicaBuiltin.mo +++ b/OMCompiler/Compiler/NFFrontEnd/NFModelicaBuiltin.mo @@ -2967,7 +2967,7 @@ function copyClass "Copies a class within the same level" input TypeName className "the class that should be copied"; input String newClassName "the name for new class"; - input TypeName withIn = $TypeName(TopLevel) "the with in path for new class"; + input TypeName withIn = $TypeName(__OpenModelica_TopLevel) "the with in path for new class"; output Boolean result; external "builtin"; annotation(preferredView="text"); diff --git a/OMCompiler/Compiler/Script/CevalScriptBackend.mo b/OMCompiler/Compiler/Script/CevalScriptBackend.mo index 29fb1b51ca8..fc494e4b3e4 100644 --- a/OMCompiler/Compiler/Script/CevalScriptBackend.mo +++ b/OMCompiler/Compiler/Script/CevalScriptBackend.mo @@ -1502,7 +1502,7 @@ algorithm case ("moveClassToBottom", _) then Values.BOOL(false); - case ("copyClass",{Values.CODE(Absyn.C_TYPENAME(classpath)), Values.STRING(name), Values.CODE(Absyn.C_TYPENAME(Absyn.IDENT("TopLevel")))}) + case ("copyClass",{Values.CODE(Absyn.C_TYPENAME(classpath)), Values.STRING(name), Values.CODE(Absyn.C_TYPENAME(Absyn.IDENT("__OpenModelica_TopLevel")))}) equation p = SymbolTable.getAbsyn(); absynClass = InteractiveUtil.getPathedClassInProgram(classpath, p); @@ -5083,8 +5083,12 @@ algorithm end match; + // Update the paths in the class to reflect the new location. + cls := NFApi.updateMovedClassPaths(inClass, inClassPath, inWithin); + // Replace the filename of each element with the new path. - cls := moveClassInfo(inClass, dst_path); + cls := moveClassInfo(cls, dst_path); + // Change the name of the class and put it in as a copy in the program. cls := AbsynUtil.setClassName(cls, inName); outProg := InteractiveUtil.updateProgram(Absyn.PROGRAM({cls}, inWithin), inProg); diff --git a/OMCompiler/Compiler/Script/NFApi.mo b/OMCompiler/Compiler/Script/NFApi.mo index 7f5734398bd..b598a2968e7 100644 --- a/OMCompiler/Compiler/Script/NFApi.mo +++ b/OMCompiler/Compiler/Script/NFApi.mo @@ -101,6 +101,7 @@ import Testsuite; import MetaModelica.Dangerous.listReverseInPlace; constant InstContext.Type ANNOTATION_CONTEXT = intBitOr(NFInstContext.RELAXED, NFInstContext.ANNOTATION); +constant InstContext.Type FAST_CONTEXT = intBitOr(NFInstContext.RELAXED, NFInstContext.FAST_LOOKUP); public function evaluateAnnotation @@ -2305,5 +2306,509 @@ algorithm jsonString := Values.STRING(JSON.toString(json, prettyPrint)); end modifierToJSON; +uniontype MoveEnv + record MOVE_ENV + InstNode scope; + Absyn.Path destinationPath; + end MOVE_ENV; +end MoveEnv; + +function updateMovedClassPaths + "Updates all the paths inside of a class that is being moved such that the + paths are still valid in the new location." + input output Absyn.Class cls "The class definition"; + input Absyn.Path clsPath "The fully qualified path of the class"; + input Absyn.Within destination "The destination package (or top scope)"; +protected + InstContext.Type context; + InstNode top, cls_node; + MoveEnv env; + Absyn.Path dest_path; +algorithm + // Make a top node and look up the class in it. + (_, top) := mkTop(SymbolTable.getAbsyn(), AbsynUtil.pathString(clsPath)); + cls_node := Inst.lookupRootClass(clsPath, top, FAST_CONTEXT); + Inst.expand(cls_node); + + // Get the destination path including the class name. + dest_path := match destination + case Absyn.Within.WITHIN() then AbsynUtil.suffixPath(destination.path, InstNode.name(cls_node)); + else Absyn.Path.IDENT(InstNode.name(cls_node)); + end match; + + env := MOVE_ENV(cls_node, dest_path); + cls.body := updateMovedClassDef(cls.body, env); +end updateMovedClassPaths; + +function updateMovedClass + input output Absyn.Class cls; + input MoveEnv env; +protected + InstNode cls_node; + MoveEnv cls_env; +algorithm + if classHasScope(cls) then + // Change the scope in the environment to this class, if the class has its + // own scope (i.e. is a long class definition). + cls_node := Lookup.lookupLocalSimpleName(cls.name, env.scope); + Inst.expand(cls_node); + cls_env := MoveEnv.MOVE_ENV(cls_node, AbsynUtil.suffixPath(env.destinationPath, cls.name)); + else + cls_env := env; + end if; + + cls.body := updateMovedClassDef(cls.body, cls_env); +end updateMovedClass; + +function classHasScope + input Absyn.Class cls; + output Boolean hasScope; +algorithm + hasScope := match cls.body + case Absyn.ClassDef.PARTS() then true; + case Absyn.ClassDef.CLASS_EXTENDS() then true; + else false; + end match; +end classHasScope; + +function updateMovedClassDef + input output Absyn.ClassDef cdef; + input MoveEnv env; +algorithm + () := match cdef + case Absyn.ClassDef.PARTS() + algorithm + cdef.classParts := list(updateMovedClassPart(p, env) for p in cdef.classParts); + cdef.ann := list(updateMovedAnnotation(a, env) for a in cdef.ann); + then + (); + + case Absyn.ClassDef.DERIVED() + algorithm + cdef.typeSpec := updateMovedTypeSpec(cdef.typeSpec, env); + cdef.attributes := updateMovedElementAttributes(cdef.attributes, env); + cdef.arguments := list(updateMovedElementArg(a, env) for a in cdef.arguments); + cdef.comment := updateMovedCommentOpt(cdef.comment, env); + then + (); + + case Absyn.ClassDef.CLASS_EXTENDS() + algorithm + cdef.modifications := list(updateMovedElementArg(a, env) for a in cdef.modifications); + cdef.parts := list(updateMovedClassPart(p, env) for p in cdef.parts); + cdef.ann := list(updateMovedAnnotation(a, env) for a in cdef.ann); + then + (); + + case Absyn.ClassDef.PDER() + algorithm + cdef.functionName := updateMovedPath(cdef.functionName, env); + cdef.comment := updateMovedCommentOpt(cdef.comment, env); + then + (); + + else (); + end match; +end updateMovedClassDef; + +function updateMovedClassPart + input output Absyn.ClassPart part; + input MoveEnv env; +algorithm + () := match part + case Absyn.ClassPart.PUBLIC() + algorithm + part.contents := list(updateMovedElementItem(i, env) for i in part.contents); + then + (); + + case Absyn.ClassPart.PROTECTED() + algorithm + part.contents := list(updateMovedElementItem(i, env) for i in part.contents); + then + (); + + case Absyn.ClassPart.EQUATIONS() + algorithm + part.contents := updateMovedEquationItems(part.contents, env); + then + (); + + case Absyn.ClassPart.INITIALEQUATIONS() + algorithm + part.contents := updateMovedEquationItems(part.contents, env); + then + (); + + case Absyn.ClassPart.ALGORITHMS() + algorithm + part.contents := updateMovedAlgorithmItems(part.contents, env); + then + (); + + case Absyn.ClassPart.INITIALALGORITHMS() + algorithm + part.contents := updateMovedAlgorithmItems(part.contents, env); + then + (); + + case Absyn.ClassPart.EXTERNAL() + algorithm + part.annotation_ := updateMovedAnnotationOpt(part.annotation_, env); + then + (); + + else (); + end match; +end updateMovedClassPart; + +function updateMovedElementItem + input output Absyn.ElementItem item; + input MoveEnv env; +algorithm + () := match item + case Absyn.ElementItem.ELEMENTITEM() + algorithm + item.element := updateMovedElement(item.element, env); + then + (); + + else (); + end match; +end updateMovedElementItem; + +function updateMovedElement + input output Absyn.Element element; + input MoveEnv env; +algorithm + () := match element + case Absyn.Element.ELEMENT() + algorithm + element.specification := updateMovedElementSpec(element.specification, env); + + if isSome(element.constrainClass) then + element.constrainClass := + SOME(updateMovedConstrainClass(Util.getOption(element.constrainClass), env)); + end if; + then + (); + + else (); + end match; +end updateMovedElement; + +function updateMovedConstrainClass + input output Absyn.ConstrainClass cc; + input MoveEnv env; +algorithm + cc.elementSpec := updateMovedElementSpec(cc.elementSpec, env); + cc.comment := updateMovedCommentOpt(cc.comment, env); +end updateMovedConstrainClass; + +function updateMovedElementSpec + input output Absyn.ElementSpec spec; + input MoveEnv env; +algorithm + () := match spec + case Absyn.ElementSpec.CLASSDEF() + algorithm + spec.class_ := updateMovedClass(spec.class_, env); + then + (); + + case Absyn.ElementSpec.EXTENDS() + algorithm + spec.path := updateMovedPath(spec.path, env); + spec.elementArg := list(updateMovedElementArg(a, env) for a in spec.elementArg); + spec.annotationOpt := updateMovedAnnotationOpt(spec.annotationOpt, env); + then + (); + + case Absyn.ElementSpec.COMPONENTS() + algorithm + spec.attributes := updateMovedElementAttributes(spec.attributes, env); + spec.typeSpec := updateMovedTypeSpec(spec.typeSpec, env); + spec.components := list(updateMovedComponentItem(c, env) for c in spec.components); + then + (); + + else (); + end match; +end updateMovedElementSpec; + +function updateMovedElementAttributes + input output Absyn.ElementAttributes attr; + input MoveEnv env; +algorithm + if not listEmpty(attr.arrayDim) then + attr.arrayDim := list(updateMovedSubscript(s, env) for s in attr.arrayDim); + end if; +end updateMovedElementAttributes; + +function updateMovedElementArg + input output Absyn.ElementArg arg; + input MoveEnv env; +algorithm + () := match arg + case Absyn.ElementArg.MODIFICATION() + algorithm + if isSome(arg.modification) then + arg.modification := SOME(updateMovedModification(Util.getOption(arg.modification), env)); + end if; + then + (); + + case Absyn.ElementArg.REDECLARATION() + algorithm + arg.elementSpec := updateMovedElementSpec(arg.elementSpec, env); + + if isSome(arg.constrainClass) then + arg.constrainClass := SOME(updateMovedConstrainClass(Util.getOption(arg.constrainClass), env)); + end if; + then + (); + + else (); + end match; +end updateMovedElementArg; + +function updateMovedModification + input output Absyn.Modification mod; + input MoveEnv env; +protected + Absyn.EqMod eq_mod; +algorithm + mod.elementArgLst := list(updateMovedElementArg(a, env) for a in mod.elementArgLst); + + eq_mod := mod.eqMod; + () := match eq_mod + case Absyn.EqMod.EQMOD() + algorithm + eq_mod.exp := updateMovedExp(eq_mod.exp, env); + mod.eqMod := eq_mod; + then + (); + + else (); + end match; +end updateMovedModification; + +function updateMovedComponentItem + input output Absyn.ComponentItem item; + input MoveEnv env; +algorithm + item.component := updateMovedComponent(item.component, env); + + if isSome(item.condition) then + item.condition := SOME(updateMovedExp(Util.getOption(item.condition), env)); + end if; +end updateMovedComponentItem; + +function updateMovedComponent + input output Absyn.Component component; + input MoveEnv env; +algorithm + if not listEmpty(component.arrayDim) then + component.arrayDim := list(updateMovedSubscript(d, env) for d in component.arrayDim); + end if; + + if isSome(component.modification) then + component.modification := SOME(updateMovedModification(Util.getOption(component.modification), env)); + end if; +end updateMovedComponent; + +function updateMovedEquationItems + input output list items; + input MoveEnv env; +algorithm + items := AbsynUtil.traverseEquationItemListBidir(items, + updateMovedExp_traverser, AbsynUtil.dummyTraverseExp, env); +end updateMovedEquationItems; + +function updateMovedAlgorithmItems + input output list items; + input MoveEnv env; +algorithm + items := AbsynUtil.traverseAlgorithmItemListBidir(items, + updateMovedExp_traverser, AbsynUtil.dummyTraverseExp, env); +end updateMovedAlgorithmItems; + +function updateMovedTypeSpec + input output Absyn.TypeSpec ty; + input MoveEnv env; +algorithm + () := match ty + case Absyn.TypeSpec.TPATH() + algorithm + ty.path := updateMovedPath(ty.path, env); + + if isSome(ty.arrayDim) then + ty.arrayDim := SOME(list(updateMovedSubscript(s, env) for s in Util.getOption(ty.arrayDim))); + end if; + then + (); + + case Absyn.TypeSpec.TCOMPLEX() + algorithm + ty.path := updateMovedPath(ty.path, env); + + if isSome(ty.arrayDim) then + ty.arrayDim := SOME(list(updateMovedSubscript(s, env) for s in Util.getOption(ty.arrayDim))); + end if; + then + (); + + end match; +end updateMovedTypeSpec; + +function updateMovedPath + input output Absyn.Path path; + input MoveEnv env; +protected + Absyn.Path qualified_path; +algorithm + // Try to look up the qualified path needed to be able to find the name in + // this scope even if the root class that contains the scope is moved elsewhere. + try + qualified_path := + Lookup.lookupSimpleNameRootPath(AbsynUtil.pathFirstIdent(path), env.scope, FAST_CONTEXT); + else + // Lookup failed, leave the path unchanged. + return; + end try; + + // If the path we found is fully qualified it means we need to update the original path. + if AbsynUtil.pathIsFullyQualified(qualified_path) then + qualified_path := AbsynUtil.makeNotFullyQualified(qualified_path); + + if AbsynUtil.pathIsQual(qualified_path) then + // If the path is qualified it needs to be joined with the original path, + // but we remove any part of the path that's the same as the destination. + qualified_path := AbsynUtil.pathStripSamePrefix(qualified_path, env.destinationPath); + + if AbsynUtil.pathIsQual(qualified_path) then + path := AbsynUtil.joinPaths(AbsynUtil.pathPrefix(qualified_path), path); + end if; + elseif AbsynUtil.pathFirstIdent(qualified_path) == AbsynUtil.pathFirstIdent(env.destinationPath) then + // Special case, the path refers to the destination package, e.g. moving path A.B.C into A. + path := AbsynUtil.pathRest(path); + end if; + end if; +end updateMovedPath; + +function updateMovedCommentOpt + input output Option cmt; + input MoveEnv env; +algorithm + if isSome(cmt) then + cmt := SOME(updateMovedComment(Util.getOption(cmt), env)); + end if; +end updateMovedCommentOpt; + +function updateMovedComment + input output Absyn.Comment cmt; + input MoveEnv env; +algorithm + cmt.annotation_ := updateMovedAnnotationOpt(cmt.annotation_, env); +end updateMovedComment; + +function updateMovedAnnotationOpt + input output Option ann; + input MoveEnv env; +algorithm + if isSome(ann) then + ann := SOME(updateMovedAnnotation(Util.getOption(ann), env)); + end if; +end updateMovedAnnotationOpt; + +function updateMovedAnnotation + input output Absyn.Annotation ann; + input MoveEnv env; +algorithm + ann.elementArgs := list(updateMovedElementArg(a, env) for a in ann.elementArgs); +end updateMovedAnnotation; + +function updateMovedSubscript + input output Absyn.Subscript sub; + input MoveEnv env; +algorithm + () := match sub + case Absyn.Subscript.SUBSCRIPT() + algorithm + sub.subscript := updateMovedExp(sub.subscript, env); + then + (); + + else (); + end match; +end updateMovedSubscript; + +function updateMovedExp + input output Absyn.Exp exp; + input MoveEnv env; +algorithm + exp := AbsynUtil.traverseExp(exp, updateMovedExp_traverser, env); +end updateMovedExp; + +function updateMovedExp_traverser + input output Absyn.Exp exp; + input output MoveEnv env; +algorithm + () := match exp + case Absyn.Exp.CREF() + algorithm + exp.componentRef := updateMovedCref(exp.componentRef, env); + then + (); + + case Absyn.Exp.CALL() + algorithm + exp.function_ := updateMovedCref(exp.function_, env); + then + (); + + else (); + end match; +end updateMovedExp_traverser; + +function updateMovedCref + input output Absyn.ComponentRef cref; + input MoveEnv env; +protected + Absyn.Path qualified_path; +algorithm + if AbsynUtil.crefIsFullyQualified(cref) or AbsynUtil.crefIsWild(cref) then + return; + end if; + + // Try to look up the qualified path needed to be able to find the name in + // this scope even if the root class that contains the scope is moved elsewhere. + try + qualified_path := + Lookup.lookupSimpleNameRootPath(AbsynUtil.crefFirstIdent(cref), env.scope, FAST_CONTEXT); + else + // Lookup failed, leave the path unchanged. + return; + end try; + + // If the path we found is fully qualified it means we need to update the original cref. + if AbsynUtil.pathIsFullyQualified(qualified_path) then + qualified_path := AbsynUtil.makeNotFullyQualified(qualified_path); + + if AbsynUtil.pathIsQual(qualified_path) then + // If the path is qualified it needs to be joined with the original cref, + // but we remove any part of the path that's the same as the destination. + qualified_path := AbsynUtil.pathStripSamePrefix(qualified_path, env.destinationPath); + + if AbsynUtil.pathIsQual(qualified_path) then + cref := AbsynUtil.joinCrefs(AbsynUtil.pathToCref(AbsynUtil.pathPrefix(qualified_path)), cref); + end if; + elseif AbsynUtil.pathFirstIdent(qualified_path) == AbsynUtil.pathFirstIdent(env.destinationPath) then + // Special case, the cref refers to the destination package, e.g. moving path A.B.C into A. + cref := AbsynUtil.crefStripFirst(cref); + end if; + end if; +end updateMovedCref; + annotation(__OpenModelica_Interface="backend"); end NFApi; diff --git a/OMEdit/OMEditLIB/OMC/OMCProxy.cpp b/OMEdit/OMEditLIB/OMC/OMCProxy.cpp index e280629f5f6..cf1bbaccba5 100644 --- a/OMEdit/OMEditLIB/OMC/OMCProxy.cpp +++ b/OMEdit/OMEditLIB/OMC/OMCProxy.cpp @@ -3214,7 +3214,7 @@ bool OMCProxy::exportToFigaro(QString className, QString directory, QString data */ bool OMCProxy::copyClass(QString className, QString newClassName, QString withIn) { - bool result = mpOMCInterface->copyClass(className, newClassName, withIn.isEmpty() ? "TopLevel" : withIn); + bool result = mpOMCInterface->copyClass(className, newClassName, withIn.isEmpty() ? "__OpenModelica_TopLevel" : withIn); if (!result) printMessagesStringInternal(); return result; } diff --git a/testsuite/openmodelica/interactive-API/CopyClass.mos b/testsuite/openmodelica/interactive-API/CopyClass.mos index b6a76200273..6ba72cee5f3 100644 --- a/testsuite/openmodelica/interactive-API/CopyClass.mos +++ b/testsuite/openmodelica/interactive-API/CopyClass.mos @@ -1,7 +1,7 @@ // name: CopyClass // keywords: copyClass // status: correct -// cflags: -d=-newInst +// cflags: -d=newInst // // Tests the copyClass API. // diff --git a/testsuite/openmodelica/interactive-API/CopyClass1.mos b/testsuite/openmodelica/interactive-API/CopyClass1.mos new file mode 100644 index 00000000000..640e4d6c881 --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClass1.mos @@ -0,0 +1,40 @@ +// name: CopyClass1 +// keywords: +// status: correct +// cflags: -d=newInst +// + +loadString(" + package P + model A + Real x; + end A; + + model B + A a; + end B; + end P; +"); + +copyClass(P.B, "C", P); +getErrorString(); +list(P); + +// Result: +// true +// true +// "" +// "package P +// model A +// Real x; +// end A; +// +// model B +// A a; +// end B; +// +// model C +// A a; +// end C; +// end P;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/CopyClass2.mos b/testsuite/openmodelica/interactive-API/CopyClass2.mos new file mode 100644 index 00000000000..993a8b626ff --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClass2.mos @@ -0,0 +1,73 @@ +// name: CopyClass2 +// keywords: +// status: correct +// cflags: -d=newInst +// + +loadString(" + package P + model A + Real x; + end A; + + function f + input Real x; + output Real y = x; + end f; + + model B + A a; + Real x = f(time); + A2 a2; + equation + a2.y = P2.P3.f(time); + algorithm + a.x := f(time); + end B; + end P; + + package P2 + model A2 + Real y; + end A2; + + package P3 + function f + input Real x; + output Real y = x; + end f; + end P3; + end P2; +"); + +copyClass(P.B, "B", P2); +getErrorString(); +list(P2); + +// Result: +// true +// true +// "" +// "package P2 +// model A2 +// Real y; +// end A2; +// +// package P3 +// function f +// input Real x; +// output Real y = x; +// end f; +// end P3; +// +// model B +// P.A a; +// Real x = P.f(time); +// A2 a2; +// equation +// a2.y = P3.f(time); +// algorithm +// a.x := P.f(time); +// end B; +// end P2;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/CopyClass3.mos b/testsuite/openmodelica/interactive-API/CopyClass3.mos new file mode 100644 index 00000000000..d24a01abb0c --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClass3.mos @@ -0,0 +1,37 @@ +// name: CopyClass3 +// keywords: +// status: correct +// cflags: -d=newInst +// + +loadString(" + package P + model A + Real x; + end A; + + model B + import P.A; + A a; + end B; + end P; + + package P2 + end P2; +"); + +copyClass(P.B, "B", P2); +getErrorString(); +list(P2); + +// Result: +// true +// true +// "" +// "package P2 +// model B +// import P.A; +// A a; +// end B; +// end P2;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/CopyClass4.mos b/testsuite/openmodelica/interactive-API/CopyClass4.mos new file mode 100644 index 00000000000..9b076544385 --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClass4.mos @@ -0,0 +1,35 @@ +// name: CopyClass4 +// keywords: +// status: correct +// cflags: -d=newInst +// +// Checks that copyClass can handle builtin names. +// + +loadString(" + package P + model A + StateSelect s = StateSelect.always; + Real x[:] = ones(3); + end A; + end P; + + package P2 + end P2; +"); + +copyClass(P.A, "A", P2); +getErrorString(); +list(P2); + +// Result: +// true +// true +// "" +// "package P2 +// model A +// StateSelect s = StateSelect.always; +// Real x[:] = ones(3); +// end A; +// end P2;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/CopyClass5.mos b/testsuite/openmodelica/interactive-API/CopyClass5.mos new file mode 100644 index 00000000000..1c53bc310d3 --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClass5.mos @@ -0,0 +1,34 @@ +// name: CopyClass5 +// keywords: +// status: correct +// cflags: -d=newInst +// +// Checks that copyClass doesn't load libraries if they're referenced, +// since such references are already fully qualified anyway. +// + +loadString(" + package P + model A + Modelica.Units.SI.Voltage v; + end A; + end P; + + package P2 + end P2; +"); + +copyClass(P.A, "A", P2); +getErrorString(); +list(P2); + +// Result: +// true +// true +// "" +// "package P2 +// model A +// Modelica.Units.SI.Voltage v; +// end A; +// end P2;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/CopyClassInvalid1.mos b/testsuite/openmodelica/interactive-API/CopyClassInvalid1.mos new file mode 100644 index 00000000000..0796315a7d7 --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClassInvalid1.mos @@ -0,0 +1,34 @@ +// name: CopyClassInvalid1 +// keywords: +// status: correct +// cflags: -d=newInst +// +// Checks that copyClass can handle references to non-existent elements. +// + +loadString(" + package P + model B + A a; + end B; + end P; +"); + +copyClass(P.B, "C", P); +getErrorString(); +list(P); + +// Result: +// true +// true +// "" +// "package P +// model B +// A a; +// end B; +// +// model C +// A a; +// end C; +// end P;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/CopyClassRedeclare1.mos b/testsuite/openmodelica/interactive-API/CopyClassRedeclare1.mos new file mode 100644 index 00000000000..dd5fa0099a5 --- /dev/null +++ b/testsuite/openmodelica/interactive-API/CopyClassRedeclare1.mos @@ -0,0 +1,40 @@ +// name: CopyClassRedeclare1 +// keywords: +// status: correct +// cflags: -d=newInst +// + +loadString(" + package P + model A + replaceable package Medium_1 end Medium_1; + end A; + + model M + package Medium end Medium; + A a(redeclare package Medium_1 = Medium_1); + end M; + end P; + + package P2 + + end P2; +"); + +copyClass(P.M, "M", P2); +getErrorString(); +list(P2); + +// Result: +// true +// true +// "" +// "package P2 +// model M +// package Medium +// end Medium; +// +// P.A a(redeclare package Medium_1 = Medium_1); +// end M; +// end P2;" +// endResult diff --git a/testsuite/openmodelica/interactive-API/Makefile b/testsuite/openmodelica/interactive-API/Makefile index 3e605db4d71..396b461da89 100644 --- a/testsuite/openmodelica/interactive-API/Makefile +++ b/testsuite/openmodelica/interactive-API/Makefile @@ -23,6 +23,13 @@ ConnectionList.mos \ ConversionVersions.mos \ ConvertUnits.mos \ CopyClass.mos \ +CopyClass1.mos \ +CopyClass2.mos \ +CopyClass3.mos \ +CopyClass4.mos \ +CopyClass5.mos \ +CopyClassInvalid1.mos \ +CopyClassRedeclare1.mos \ DefaultComponentName.mos \ DeleteConnection.mos \ DialogAnnotation.mos \