OMCompiler/Compiler/FrontEnd/InnerOuter.mo
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of OpenModelica. | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), | ||
| 5 | * c/o Linköpings universitet, Department of Computer and Information Science, | ||
| 6 | * SE-58183 Linköping, Sweden. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR | ||
| 11 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. | ||
| 12 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES | ||
| 13 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL | ||
| 14 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 15 | * | ||
| 16 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) | ||
| 17 | * Public License (OSMC-PL) are obtained from OSMC, either from the above | ||
| 18 | * address, from the URLs: | ||
| 19 | * http://www.openmodelica.org or | ||
| 20 | * https://github.com/OpenModelica/ or | ||
| 21 | * http://www.ida.liu.se/projects/OpenModelica, | ||
| 22 | * and in the OpenModelica distribution. | ||
| 23 | * | ||
| 24 | * GNU AGPL version 3 is obtained from: | ||
| 25 | * https://www.gnu.org/licenses/licenses.html#GPL | ||
| 26 | * | ||
| 27 | * This program is distributed WITHOUT ANY WARRANTY; without | ||
| 28 | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 29 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH | ||
| 30 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. | ||
| 31 | * | ||
| 32 | * See the full OSMC Public License conditions for more details. | ||
| 33 | * | ||
| 34 | */ | ||
| 35 | |||
| 36 | encapsulated package InnerOuter | ||
| 37 | " file: InnerOuter.mo | ||
| 38 | package: InnerOuter | ||
| 39 | description: Instance hierarchy and functionality to deal with Inner/Outer definitions" | ||
| 40 | |||
| 41 | |||
| 42 | import Absyn; | ||
| 43 | import DAE.Connect; | ||
| 44 | import ConnectionGraph; | ||
| 45 | import DAE; | ||
| 46 | import FCore; | ||
| 47 | import FNode; | ||
| 48 | import SCode; | ||
| 49 | import UnitAbsyn; | ||
| 50 | import HashSet; | ||
| 51 | |||
| 52 | protected import Array; | ||
| 53 | protected import ComponentReference; | ||
| 54 | protected import ComponentReferenceBasics; | ||
| 55 | protected import ConnectUtil; | ||
| 56 | protected import DAEUtil; | ||
| 57 | protected import Debug; | ||
| 58 | protected import ElementSource; | ||
| 59 | protected import Error; | ||
| 60 | protected import ErrorExt; | ||
| 61 | protected import Flags; | ||
| 62 | protected import InstSection; | ||
| 63 | protected import List; | ||
| 64 | protected import Lookup; | ||
| 65 | protected import Mod; | ||
| 66 | protected import PrefixUtil; | ||
| 67 | protected import Util; | ||
| 68 | protected import BaseHashSet; | ||
| 69 | protected import FGraph; | ||
| 70 | |||
| 71 | public | ||
| 72 | type Cache = FCore.Cache; | ||
| 73 | |||
| 74 | uniontype InstResult | ||
| 75 | record INST_RESULT | ||
| 76 | Cache outCache; | ||
| 77 | FCore.Graph outEnv; | ||
| 78 | UnitAbsyn.InstStore outStore; | ||
| 79 | DAE.DAElist outDae; | ||
| 80 | Connect.Sets outSets; | ||
| 81 | DAE.Type outType; | ||
| 82 | ConnectionGraph.ConnectionGraph outGraph; | ||
| 83 | end INST_RESULT; | ||
| 84 | end InstResult; | ||
| 85 | |||
| 86 | uniontype InstInner | ||
| 87 | record INST_INNER | ||
| 88 | DAE.Prefix innerPrefix "the prefix of the inner. we need it to prefix the outer variables with it!"; | ||
| 89 | SCode.Ident name; | ||
| 90 | Absyn.InnerOuter io; | ||
| 91 | String fullName "full inner component name"; | ||
| 92 | Absyn.Path typePath "the type of the inner"; | ||
| 93 | String scope "the scope of the inner"; | ||
| 94 | Option<InstResult> instResult; | ||
| 95 | list<DAE.ComponentRef> outers "which outers are referencing this inner"; | ||
| 96 | Option<SCode.Element> innerElement "class or component"; | ||
| 97 | end INST_INNER; | ||
| 98 | end InstInner; | ||
| 99 | |||
| 100 | uniontype OuterPrefix | ||
| 101 | record OUTER | ||
| 102 | DAE.ComponentRef outerComponentRef "the prefix of this outer + component name"; | ||
| 103 | DAE.ComponentRef innerComponentRef "the coresponding prefix for this outer + component name"; | ||
| 104 | end OUTER; | ||
| 105 | end OuterPrefix; | ||
| 106 | |||
| 107 | type OuterPrefixes = list<OuterPrefix>; | ||
| 108 | |||
| 109 | constant OuterPrefixes emptyOuterPrefixes = {} "empty outer prefixes"; | ||
| 110 | |||
| 111 | public | ||
| 112 | type Key = DAE.ComponentRef "the prefix + '.' + the component name"; | ||
| 113 | type Value = InstInner "the inputs of the instantiation function and the results"; | ||
| 114 | |||
| 115 | uniontype TopInstance "a top instance is an instance of a model thar resides at top level" | ||
| 116 | record TOP_INSTANCE | ||
| 117 | Option<Absyn.Path> path "top model path"; | ||
| 118 | InstHierarchyHashTable ht "hash table with fully qualified components"; | ||
| 119 | OuterPrefixes outerPrefixes "the outer prefixes help us prefix the outer components with the correct prefix of inner component directly"; | ||
| 120 | HashSet.HashSet sm "Set of synchronous SM states (fully qualified components)"; | ||
| 121 | end TOP_INSTANCE; | ||
| 122 | end TopInstance; | ||
| 123 | |||
| 124 | type InstHierarchy = list<TopInstance>; | ||
| 125 | |||
| 126 | constant InstHierarchy emptyInstHierarchy = {} | ||
| 127 | "an empty instance hierarchy"; | ||
| 128 | |||
| 129 | public function handleInnerOuterEquations | ||
| 130 | "Author: BZ, 2008-12 | ||
| 131 | Depending on the inner outer declaration we do | ||
| 132 | different things for dae declared for a variable. | ||
| 133 | If it is an outer variable, we remove all equations | ||
| 134 | (will be declared again in the inner part). | ||
| 135 | If it is InnerOuter declared, we rename all the crefs | ||
| 136 | in this equation to unique vars, while we want to keep | ||
| 137 | them with this prefix for the inner part of the innerouter." | ||
| 138 | input Absyn.InnerOuter io; | ||
| 139 | input DAE.DAElist inDae; | ||
| 140 | input InstHierarchy inIH; | ||
| 141 | input ConnectionGraph.ConnectionGraph inGraphNew; | ||
| 142 | input ConnectionGraph.ConnectionGraph inGraph; | ||
| 143 | output DAE.DAElist odae; | ||
| 144 | output InstHierarchy outIH; | ||
| 145 | output ConnectionGraph.ConnectionGraph outGraph; | ||
| 146 | algorithm | ||
| 147 | (odae,outIH,outGraph) := matchcontinue(io,inDae,inIH,inGraphNew,inGraph) | ||
| 148 | local | ||
| 149 | DAE.DAElist dae1,dae2,dae; | ||
| 150 | ConnectionGraph.ConnectionGraph graphNew,graph; | ||
| 151 | InstHierarchy ih; | ||
| 152 | // is an outer, remove equations | ||
| 153 | // outer components do NOT change the connection graph! | ||
| 154 | case (Absyn.OUTER(),dae,ih,_,graph) | ||
| 155 | algorithm | ||
| 156 | 697 | (odae,_) := DAEUtil.splitDAEIntoVarsAndEquations(dae); | |
| 157 | then | ||
| 158 | (odae,ih,graph); | ||
| 159 | // is both an inner and an outer, | ||
| 160 | // rename inner vars in the equations to unique names | ||
| 161 | // innerouter component change the connection graph | ||
| 162 | case (Absyn.INNER_OUTER(),dae,ih,_,graph) | ||
| 163 | algorithm | ||
| 164 | 4 | (dae1,dae2) := DAEUtil.splitDAEIntoVarsAndEquations(dae); | |
| 165 | // rename variables in the equations and algs. | ||
| 166 | // inner vars from dae1 are kept with the same name. | ||
| 167 | 4 | dae2 := DAEUtil.nameUniqueOuterVars(dae2); | |
| 168 | |||
| 169 | 4 | dae := DAEUtil.joinDaes(dae1,dae2); | |
| 170 | // adrpo: TODO! FIXME: here we should do a difference of graphNew-graph | ||
| 171 | // and rename the new equations added with unique vars. | ||
| 172 | then | ||
| 173 | (dae,ih,graph); | ||
| 174 | // is an inner do nothing | ||
| 175 | case (Absyn.INNER(),dae,ih,graphNew,_) then (dae,ih,graphNew); | ||
| 176 | // is not an inner nor an outer | ||
| 177 | case (Absyn.NOT_INNER_OUTER (),dae,ih,graphNew,_) then (dae,ih,graphNew); | ||
| 178 | // something went totally wrong! | ||
| 179 | else | ||
| 180 | algorithm | ||
| 181 | ✗ | print("- InnerOuter.handleInnerOuterEquations failed!\n"); | |
| 182 | ✗ | then fail(); | |
| 183 | end matchcontinue; | ||
| 184 | end handleInnerOuterEquations; | ||
| 185 | |||
| 186 | public function changeInnerOuterInOuterConnect | ||
| 187 | "changes inner to outer and outer to inner where needed" | ||
| 188 | input output Connect.Sets sets; | ||
| 189 | algorithm | ||
| 190 | 159882 | sets.outerConnects := List.map(sets.outerConnects, changeInnerOuterInOuterConnect2); | |
| 191 | end changeInnerOuterInOuterConnect; | ||
| 192 | |||
| 193 | protected function changeInnerOuterInOuterConnect2 | ||
| 194 | "@author: adrpo | ||
| 195 | changes inner to outer and outer to inner where needed" | ||
| 196 | input Connect.OuterConnect inOC; | ||
| 197 | output Connect.OuterConnect outOC; | ||
| 198 | algorithm | ||
| 199 | outOC := matchcontinue inOC | ||
| 200 | local | ||
| 201 | DAE.ComponentRef cr1,cr2,ncr1,ncr2; | ||
| 202 | Absyn.InnerOuter io1,io2; | ||
| 203 | Connect.Face f1,f2; | ||
| 204 | DAE.Prefix scope; | ||
| 205 | DAE.ElementSource source "the origin of the element"; | ||
| 206 | |||
| 207 | // the left hand side is an outer! | ||
| 208 | case Connect.OUTERCONNECT(scope,cr1,io1,f1,cr2,io2,f2,source) | ||
| 209 | algorithm | ||
| 210 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 8 times.
|
22 | (_,true) := innerOuterBooleans(io1); |
| 211 | 8 | ncr1 := PrefixUtil.prefixToCref(scope); | |
| 212 | // fprintln(Flags.IOS, "changeInnerOuterInOuterConnect: changing left: " + | ||
| 213 | // ComponentReferenceBasics.printComponentRefStr(cr1) + " to inner"); | ||
| 214 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 | false := ComponentReferenceBasics.crefFirstCrefLastCrefEqual(ncr1,cr1); |
| 215 | 8 | then | |
| 216 | Connect.OUTERCONNECT(scope,cr1,Absyn.INNER(),f1,cr2,io2,f2,source); | ||
| 217 | |||
| 218 | // the right hand side is an outer! | ||
| 219 | case Connect.OUTERCONNECT(scope,cr1,io1,f1,cr2,io2,f2,source) | ||
| 220 | algorithm | ||
| 221 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
|
14 | (_,true) := innerOuterBooleans(io2); |
| 222 | 2 | ncr2 := PrefixUtil.prefixToCref(scope); | |
| 223 | // fprintln(Flags.IOS, "changeInnerOuterInOuterConnect: changing right: " + | ||
| 224 | // ComponentReferenceBasics.printComponentRefStr(cr2) + " to inner"); | ||
| 225 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | false := ComponentReferenceBasics.crefFirstCrefLastCrefEqual(ncr2,cr2); |
| 226 | 2 | then | |
| 227 | Connect.OUTERCONNECT(scope,cr1,io1,f1,cr2,Absyn.INNER(),f2,source); | ||
| 228 | |||
| 229 | // none of left or right hand side are outer | ||
| 230 | else inOC; | ||
| 231 | end matchcontinue; | ||
| 232 | end changeInnerOuterInOuterConnect2; | ||
| 233 | |||
| 234 | public function retrieveOuterConnections | ||
| 235 | "Moves outerConnections to connection sets | ||
| 236 | author PA: | ||
| 237 | This function moves the connections put in outerConnects to the connection | ||
| 238 | set, if a corresponding innner component can be found in the environment. | ||
| 239 | If not, they are kept in the outerConnects for use higher up in the instance | ||
| 240 | hierarchy." | ||
| 241 | input FCore.Cache inCache; | ||
| 242 | input FCore.Graph inEnv; | ||
| 243 | input InstHierarchy inIH; | ||
| 244 | input DAE.Prefix inPrefix; | ||
| 245 | input Connect.Sets inSets; | ||
| 246 | input Boolean inTopCall; | ||
| 247 | input ConnectionGraph.ConnectionGraph inCGraph; | ||
| 248 | output Connect.Sets outSets; | ||
| 249 | output list<Connect.OuterConnect> outInnerOuterConnects; | ||
| 250 | output ConnectionGraph.ConnectionGraph outCGraph; | ||
| 251 | protected | ||
| 252 | list<Connect.OuterConnect> oc; | ||
| 253 | algorithm | ||
| 254 | 898893 | Connect.SETS(outerConnects = oc) := inSets; | |
| 255 | 898893 | (oc, outSets, outInnerOuterConnects, outCGraph) := | |
| 256 | retrieveOuterConnections2(inCache, inEnv, inIH, inPrefix, oc, inSets, inTopCall, inCGraph); | ||
| 257 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 898893 times.
|
898893 | outSets.outerConnects := oc; |
| 258 | end retrieveOuterConnections; | ||
| 259 | |||
| 260 | protected function removeInnerPrefixFromCref | ||
| 261 | "@author: adrpo | ||
| 262 | This function will strip the given prefix from the component references." | ||
| 263 | input DAE.Prefix inPrefix; | ||
| 264 | input DAE.ComponentRef inCref; | ||
| 265 | output DAE.ComponentRef outCref; | ||
| 266 | algorithm | ||
| 267 | outCref := matchcontinue inPrefix | ||
| 268 | local | ||
| 269 | DAE.ComponentRef crefPrefix, crOuter; | ||
| 270 | |||
| 271 | // no prefix to strip, return the cref! | ||
| 272 | case DAE.NOPRE() then inCref; | ||
| 273 | |||
| 274 | // we have a prefix, remove it from the cref | ||
| 275 | case _ | ||
| 276 | algorithm | ||
| 277 | // transform prefix into cref | ||
| 278 | ✗ | crefPrefix := PrefixUtil.prefixToCref(inPrefix); | |
| 279 | // remove the prefix from the component reference | ||
| 280 | ✗ | crOuter := ComponentReference.crefStripPrefix(inCref, crefPrefix); | |
| 281 | then | ||
| 282 | crOuter; | ||
| 283 | |||
| 284 | // something went wrong, print a failtrace and then | ||
| 285 | else | ||
| 286 | algorithm | ||
| 287 | //true = Flags.isSet(Flags.FAILTRACE); | ||
| 288 | //Debug.traceln("- InnerOuter.removeInnerPrefixFromCref failed on prefix: " + PrefixUtil.printPrefixStr(inPrefix) + | ||
| 289 | // " cref: " + ComponentReferenceBasics.printComponentRefStr(inCref)); | ||
| 290 | then | ||
| 291 | inCref; | ||
| 292 | end matchcontinue; | ||
| 293 | end removeInnerPrefixFromCref; | ||
| 294 | |||
| 295 | protected function retrieveOuterConnections2 | ||
| 296 | "help function to retrieveOuterConnections" | ||
| 297 | input FCore.Cache inCache; | ||
| 298 | input FCore.Graph inEnv; | ||
| 299 | input InstHierarchy inIH; | ||
| 300 | input DAE.Prefix inPrefix; | ||
| 301 | input list<Connect.OuterConnect> inOuterConnects; | ||
| 302 | input Connect.Sets inSets; | ||
| 303 | input Boolean inTopCall; | ||
| 304 | input ConnectionGraph.ConnectionGraph inCGraph; | ||
| 305 | output list<Connect.OuterConnect> outOuterConnects; | ||
| 306 | output Connect.Sets outSets; | ||
| 307 | output list<Connect.OuterConnect> outInnerOuterConnects; | ||
| 308 | output ConnectionGraph.ConnectionGraph outCGraph; | ||
| 309 | algorithm | ||
| 310 | (outOuterConnects, outSets, outInnerOuterConnects, outCGraph) := | ||
| 311 | matchcontinue(inOuterConnects, inSets, inTopCall, inCGraph) | ||
| 312 | local | ||
| 313 | DAE.ComponentRef cr1, cr2; | ||
| 314 | Absyn.InnerOuter io1, io2; | ||
| 315 | Connect.Face f1, f2; | ||
| 316 | Connect.OuterConnect oc; | ||
| 317 | list<Connect.OuterConnect> rest_oc, ioc; | ||
| 318 | Boolean inner1, inner2, outer1, outer2, added; | ||
| 319 | DAE.Prefix scope; | ||
| 320 | DAE.ElementSource source "the origin of the element"; | ||
| 321 | SourceInfo info; | ||
| 322 | Connect.Sets sets; | ||
| 323 | ConnectionGraph.ConnectionGraph graph; | ||
| 324 | |||
| 325 | // handle empty | ||
| 326 | 898893 | case ({}, _, _, _) then (inOuterConnects, inSets, {}, inCGraph); | |
| 327 | |||
| 328 | // an inner only outer connect | ||
| 329 | case(Connect.OUTERCONNECT(scope, cr1, io1, f1, cr2, io2, f2, | ||
| 330 | source as DAE.SOURCE(info = info)) :: rest_oc, sets, _, graph) | ||
| 331 | algorithm | ||
| 332 | 27 | (inner1, outer1) := lookupVarInnerOuterAttr(inCache, inEnv, inIH, cr1, cr2); | |
| 333 | |||
| 334 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 7 times.
|
20 | true := inner1; |
| 335 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
|
7 | false := outer1; |
| 336 | |||
| 337 | // remove the prefixes so we can find it in the DAE | ||
| 338 | 4 | cr1 := removeInnerPrefixFromCref(inPrefix, cr1); | |
| 339 | 4 | cr2 := removeInnerPrefixFromCref(inPrefix, cr2); | |
| 340 | |||
| 341 | 4 | (sets, added) := ConnectUtil.addOuterConnectToSets(cr1, cr2, io1, io2, f1, f2, sets, info); | |
| 342 | |||
| 343 | // if no connection set available (added = false), create new one | ||
| 344 | 4 | (sets, graph) := addOuterConnectIfEmpty(inCache, inEnv, inIH, inPrefix, sets, | |
| 345 | added, cr1, io1, f1, cr2, io2, f2, info, graph); | ||
| 346 | |||
| 347 | 4 | (rest_oc, sets, ioc, graph) := | |
| 348 | retrieveOuterConnections2(inCache, inEnv, inIH, inPrefix, rest_oc, sets, inTopCall, graph); | ||
| 349 | |||
| 350 | // if is also outer, then keep it also in the outer connects | ||
| 351 | rest_oc := if outer1 then Connect.OUTERCONNECT(scope, cr1, io1, f1, cr2, io2, f2, source) :: rest_oc else rest_oc; | ||
| 352 | 4 | then | |
| 353 | (rest_oc, sets, ioc, graph); | ||
| 354 | |||
| 355 | // this case is for innerouter declarations, since we do not have them in environment we need to treat them in a special way | ||
| 356 | case(Connect.OUTERCONNECT(_, cr1, io1, f1, cr2, io2, f2, | ||
| 357 | DAE.SOURCE(info = info)) :: rest_oc, sets, true, graph) | ||
| 358 | algorithm | ||
| 359 | 6 | (inner1, outer1) := innerOuterBooleans(io1); | |
| 360 | 6 | (inner2, outer2) := innerOuterBooleans(io2); | |
| 361 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | true := boolOr(inner1, inner2); // for inner outer we set Absyn.INNER() |
| 362 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | false := boolOr(outer1, outer2); |
| 363 | |||
| 364 | 6 | io1 := convertInnerOuterInnerToOuter(io1); // we need to change from inner to outer to be able to join sets in: addOuterConnectToSets | |
| 365 | 6 | io2 := convertInnerOuterInnerToOuter(io2); | |
| 366 | |||
| 367 | 6 | (sets, added) := ConnectUtil.addOuterConnectToSets(cr1, cr2, io1, io2, f1, f2, sets, info); | |
| 368 | // If no connection set available (added = false), create new one | ||
| 369 | 6 | (sets, graph) := addOuterConnectIfEmpty(inCache, inEnv, inIH, inPrefix, sets, | |
| 370 | added, cr1, io1, f1, cr2, io2, f2, info, graph); | ||
| 371 | 6 | (rest_oc, sets, ioc, graph) := | |
| 372 | retrieveOuterConnections2(inCache, inEnv, inIH, inPrefix, rest_oc, sets, true, graph); | ||
| 373 | then | ||
| 374 | (rest_oc, sets, ioc, graph); | ||
| 375 | |||
| 376 | // just keep the outer connects the same if we don't find them in the same scope | ||
| 377 | case(oc :: rest_oc, sets, _, graph) | ||
| 378 | algorithm | ||
| 379 | 17 | (rest_oc, sets, ioc, graph) := | |
| 380 | retrieveOuterConnections2(inCache, inEnv, inIH, inPrefix, rest_oc, sets, inTopCall, graph); | ||
| 381 | 17 | then | |
| 382 | (oc :: rest_oc, sets, ioc, graph); | ||
| 383 | end matchcontinue; | ||
| 384 | end retrieveOuterConnections2; | ||
| 385 | |||
| 386 | protected function convertInnerOuterInnerToOuter | ||
| 387 | "Author: BZ, 2008-12 | ||
| 388 | Change from Absyn.INNER => Absyn.OUTER, | ||
| 389 | this to be able to use normal functions | ||
| 390 | for the innerouter declared variables/connections." | ||
| 391 | input Absyn.InnerOuter io; | ||
| 392 | output Absyn.InnerOuter oio; | ||
| 393 | algorithm | ||
| 394 | oio := match io | ||
| 395 | case Absyn.INNER() then Absyn.OUTER(); | ||
| 396 | else io; | ||
| 397 | end match; | ||
| 398 | end convertInnerOuterInnerToOuter; | ||
| 399 | |||
| 400 | protected function addOuterConnectIfEmpty | ||
| 401 | "help function to retrieveOuterConnections2 | ||
| 402 | author PA. | ||
| 403 | Adds a new connectionset if inner component | ||
| 404 | found but no connection set refering to the | ||
| 405 | inner component. In that is case the outer | ||
| 406 | connection (from inside sub-components) forms | ||
| 407 | a connection set of their own." | ||
| 408 | input FCore.Cache inCache; | ||
| 409 | input FCore.Graph inEnv; | ||
| 410 | input InstHierarchy inIH; | ||
| 411 | input DAE.Prefix pre; | ||
| 412 | input Connect.Sets inSets; | ||
| 413 | input Boolean added "if true, this function does nothing"; | ||
| 414 | input DAE.ComponentRef cr1; | ||
| 415 | input Absyn.InnerOuter iio1; | ||
| 416 | input Connect.Face f1; | ||
| 417 | input DAE.ComponentRef cr2; | ||
| 418 | input Absyn.InnerOuter iio2; | ||
| 419 | input Connect.Face f2; | ||
| 420 | input SourceInfo info; | ||
| 421 | input ConnectionGraph.ConnectionGraph inCGraph; | ||
| 422 | output Connect.Sets outSets; | ||
| 423 | output ConnectionGraph.ConnectionGraph outCGraph; | ||
| 424 | algorithm | ||
| 425 | (outSets, outCGraph) := match(inCache, inEnv, inIH, inSets, added, iio1, iio2, inCGraph) | ||
| 426 | local | ||
| 427 | SCode.Variability vt1,vt2; | ||
| 428 | DAE.Type t1,t2; | ||
| 429 | DAE.ConnectorType ct; | ||
| 430 | InstHierarchy ih; | ||
| 431 | Connect.SetTrie sets; | ||
| 432 | Integer sc; | ||
| 433 | list<Connect.SetConnection> cl; | ||
| 434 | list<Connect.OuterConnect> oc; | ||
| 435 | FCore.Cache cache; | ||
| 436 | FCore.Graph env; | ||
| 437 | Absyn.InnerOuter io1,io2; | ||
| 438 | ConnectionGraph.ConnectionGraph graph; | ||
| 439 | |||
| 440 | // if it was added, return the same | ||
| 441 | case(_, _, _, _, true, _, _, _) | ||
| 442 | then (inSets, inCGraph); | ||
| 443 | |||
| 444 | // if it was not added, add it (search for both components) | ||
| 445 | case(cache, env, ih, Connect.SETS(sets, sc, cl, oc), false, io1, io2, graph) | ||
| 446 | algorithm | ||
| 447 | 10 | (cache,DAE.ATTR(connectorType = ct, variability = vt1),t1,_,_,_,_,_,_) := Lookup.lookupVar(cache,env,cr1); | |
| 448 | 10 | (cache,DAE.ATTR(variability = vt2),t2,_,_,_,_,_,_) := Lookup.lookupVar(cache,env,cr2); | |
| 449 | 10 | io1 := removeOuter(io1); | |
| 450 | 10 | io2 := removeOuter(io2); | |
| 451 | 10 | (cache,env,ih, Connect.SETS(sets = sets, setCount = sc, connections = cl),_, graph) := | |
| 452 | InstSection.connectComponents( | ||
| 453 | cache,env,ih, | ||
| 454 | Connect.SETS(sets, sc, cl, {}), | ||
| 455 | pre,cr1,f1,t1,vt1,cr2,f2,t2,vt2,ct,io1,io2, | ||
| 456 | graph,info); | ||
| 457 | // TODO: take care of dae, can contain asserts from connections | ||
| 458 | 10 | then | |
| 459 | (Connect.SETS(sets, sc, cl, oc), graph); | ||
| 460 | |||
| 461 | // This can fail, for innerouter, the inner part is not declared in env so instead the call to addOuterConnectIfEmptyNoEnv will succed. | ||
| 462 | else | ||
| 463 | algorithm | ||
| 464 | //print("Failed lookup: " + ComponentReferenceBasics.printComponentRefStr(cr1) + "\n"); | ||
| 465 | //print("Failed lookup: " + ComponentReferenceBasics.printComponentRefStr(cr2) + "\n"); | ||
| 466 | // print("#FAILURE# in: addOuterConnectIfEmpty:__ " + ComponentReferenceBasics.printComponentRefStr(cr1) + " " + ComponentReferenceBasics.printComponentRefStr(cr2) + "\n"); | ||
| 467 | then fail(); | ||
| 468 | |||
| 469 | end match; | ||
| 470 | end addOuterConnectIfEmpty; | ||
| 471 | |||
| 472 | protected function removeOuter | ||
| 473 | "Removes outer attribute, keeping inner" | ||
| 474 | input Absyn.InnerOuter io; | ||
| 475 | output Absyn.InnerOuter outIo; | ||
| 476 | algorithm | ||
| 477 | outIo := match io | ||
| 478 | case Absyn.OUTER() then Absyn.NOT_INNER_OUTER(); | ||
| 479 | case Absyn.INNER() then Absyn.INNER(); | ||
| 480 | case Absyn.INNER_OUTER() then Absyn.INNER(); | ||
| 481 | case Absyn.NOT_INNER_OUTER() then Absyn.NOT_INNER_OUTER(); | ||
| 482 | end match; | ||
| 483 | end removeOuter; | ||
| 484 | |||
| 485 | protected function lookupVarInnerOuterAttr | ||
| 486 | "searches for two variables in env and retrieves | ||
| 487 | its inner and outer attributes in form of booleans. | ||
| 488 | adrpo: Make sure that there are no error messages displayed!" | ||
| 489 | input FCore.Cache cache; | ||
| 490 | input FCore.Graph env; | ||
| 491 | input InstHierarchy inIH; | ||
| 492 | input DAE.ComponentRef cr1; | ||
| 493 | input DAE.ComponentRef cr2; | ||
| 494 | output Boolean isInner; | ||
| 495 | output Boolean isOuter; | ||
| 496 | algorithm | ||
| 497 | (isInner,isOuter) := matchcontinue cr2 | ||
| 498 | local | ||
| 499 | Absyn.InnerOuter io,io1,io2; | ||
| 500 | Boolean isInner1,isInner2,isOuter1,isOuter2; | ||
| 501 | // Search for both | ||
| 502 | case _ | ||
| 503 | algorithm | ||
| 504 | 27 | ErrorExt.setCheckpoint("lookupVarInnerOuterAttr"); | |
| 505 | 27 | (_,DAE.ATTR(innerOuter=io1),_,_,_,_,_,_,_) := Lookup.lookupVar(cache,env,cr1); | |
| 506 | 17 | (_,DAE.ATTR(innerOuter=io2),_,_,_,_,_,_,_) := Lookup.lookupVar(cache,env,cr2); | |
| 507 | 10 | (isInner1,isOuter1) := innerOuterBooleans(io1); | |
| 508 | 10 | (isInner2,isOuter2) := innerOuterBooleans(io2); | |
| 509 | 10 | isInner := isInner1 or isInner2; | |
| 510 |
3/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
10 | isOuter := isOuter1 or isOuter2; |
| 511 | 10 | ErrorExt.rollBack("lookupVarInnerOuterAttr"); | |
| 512 | then | ||
| 513 | (isInner,isOuter); | ||
| 514 | // try to find var cr1 (lookup can fail for one of them) | ||
| 515 | case _ | ||
| 516 | algorithm | ||
| 517 | 17 | (_,DAE.ATTR(innerOuter=io),_,_,_,_,_,_,_) := Lookup.lookupVar(cache,env,cr1); | |
| 518 | 7 | (isInner,isOuter) := innerOuterBooleans(io); | |
| 519 | 7 | ErrorExt.rollBack("lookupVarInnerOuterAttr"); | |
| 520 | 7 | then | |
| 521 | (isInner,isOuter); | ||
| 522 | // ..else try cr2 (lookup can fail for one of them) | ||
| 523 | case _ | ||
| 524 | algorithm | ||
| 525 | 10 | (_,DAE.ATTR(innerOuter=io),_,_,_,_,_,_,_) := Lookup.lookupVar(cache,env,cr2); | |
| 526 | 3 | (isInner,isOuter) := innerOuterBooleans(io); | |
| 527 | 3 | ErrorExt.rollBack("lookupVarInnerOuterAttr"); | |
| 528 | 3 | then (isInner,isOuter); | |
| 529 | // failure | ||
| 530 | else | ||
| 531 | algorithm | ||
| 532 | 7 | ErrorExt.rollBack("lookupVarInnerOuterAttr"); | |
| 533 | 7 | then fail(); | |
| 534 | end matchcontinue; | ||
| 535 | end lookupVarInnerOuterAttr; | ||
| 536 | |||
| 537 | protected function innerOuterBooleans | ||
| 538 | "Returns inner outer information as two booleans" | ||
| 539 | input Absyn.InnerOuter io; | ||
| 540 | output Boolean inner1; | ||
| 541 | output Boolean outer1; | ||
| 542 | algorithm | ||
| 543 | (inner1,outer1) := match io | ||
| 544 | case Absyn.INNER() then (true,false); | ||
| 545 | case Absyn.OUTER() then (false,true); | ||
| 546 | case Absyn.INNER_OUTER() then (true,true); | ||
| 547 | case Absyn.NOT_INNER_OUTER() then (false,false); | ||
| 548 | end match; | ||
| 549 | end innerOuterBooleans; | ||
| 550 | |||
| 551 | public function outerConnection "Returns true if either Absyn.InnerOuter is OUTER." | ||
| 552 | input Absyn.InnerOuter io1; | ||
| 553 | input Absyn.InnerOuter io2; | ||
| 554 | output Boolean isOuter; | ||
| 555 | algorithm | ||
| 556 | isOuter := match(io1,io2) | ||
| 557 | case(Absyn.OUTER(),_) then true; | ||
| 558 | case(_,Absyn.OUTER()) then true; | ||
| 559 | case(Absyn.INNER_OUTER(),_) then true; | ||
| 560 | case(_,Absyn.INNER_OUTER()) then true; | ||
| 561 | else false; | ||
| 562 | end match; | ||
| 563 | end outerConnection; | ||
| 564 | |||
| 565 | protected function lookupInnerInIH | ||
| 566 | "@author: adrpo | ||
| 567 | Given an instance hierarchy and a component name find the | ||
| 568 | modification of the inner component with the same name" | ||
| 569 | input TopInstance inTIH; | ||
| 570 | input DAE.Prefix inPrefix; | ||
| 571 | input SCode.Ident inComponentIdent; | ||
| 572 | output InstInner outInstInner; | ||
| 573 | algorithm | ||
| 574 | outInstInner := matchcontinue(inTIH, inPrefix, inComponentIdent) | ||
| 575 | local | ||
| 576 | SCode.Ident name; | ||
| 577 | DAE.Prefix prefix; | ||
| 578 | InstHierarchyHashTable ht; | ||
| 579 | DAE.ComponentRef cref; | ||
| 580 | InstInner instInner; | ||
| 581 | |||
| 582 | // no prefix, this is an error! | ||
| 583 | // disabled as this is used in Interactive.getComponents | ||
| 584 | // and makes mosfiles/interactive_api_attributes.mos to fail! | ||
| 585 | case (TOP_INSTANCE(), DAE.PREFIX(compPre = DAE.NOCOMPPRE()), _) | ||
| 586 | ✗ | then lookupInnerInIH(inTIH, DAE.NOPRE(), inComponentIdent); | |
| 587 | |||
| 588 | // no prefix, this is an error! | ||
| 589 | // disabled as this is used in Interactive.getComponents | ||
| 590 | // and makes mosfiles/interactive_api_attributes.mos to fail! | ||
| 591 | case (TOP_INSTANCE(), DAE.NOPRE(), name) | ||
| 592 | algorithm | ||
| 593 | // fprintln(Flags.INNER_OUTER, "Error: outer component: " + name + " defined at the top level!"); | ||
| 594 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : looking for: " + PrefixUtil.printPrefixStr(DAE.NOPRE()) + "/" + name + " REACHED TOP LEVEL!"); | ||
| 595 | // TODO! add warning! | ||
| 596 | ✗ | then emptyInstInner(DAE.NOPRE(), name); | |
| 597 | |||
| 598 | // we have a prefix, remove the last cref from the prefix and search! | ||
| 599 | case (TOP_INSTANCE(_, ht, _, _), _, name) | ||
| 600 | algorithm | ||
| 601 | // back one step in the instance hierarchy | ||
| 602 | |||
| 603 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : looking for: " + PrefixUtil.printPrefixStr(inPrefix) + "/" + name); | ||
| 604 | |||
| 605 | 1190 | prefix := PrefixUtil.prefixStripLast(inPrefix); | |
| 606 | |||
| 607 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : stripping and looking for: " + PrefixUtil.printPrefixStr(prefix) + "/" + name); | ||
| 608 | |||
| 609 | // put the name as the last prefix | ||
| 610 | 1190 | (_,cref) := PrefixUtil.prefixCref(FCore.emptyCache(), FGraph.empty(), emptyInstHierarchy, prefix, ComponentReferenceBasics.makeCrefIdent(name, DAE.T_UNKNOWN_DEFAULT, {})); | |
| 611 | |||
| 612 | // search in instance hierarchy | ||
| 613 | 1190 | instInner := get(cref, ht); | |
| 614 | |||
| 615 | // isInner = AbsynUtil.isInner(io); | ||
| 616 | // instInner = if_(isInner, instInner, emptyInstInner(inPrefix, name)); | ||
| 617 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : Looking up: " + | ||
| 618 | // ComponentReferenceBasics.printComponentRefStr(cref) + " FOUND with innerPrefix: " + | ||
| 619 | // PrefixUtil.printPrefixStr(innerPrefix)); | ||
| 620 | then | ||
| 621 | instInner; | ||
| 622 | |||
| 623 | // we have a prefix, search recursively as there was a failure before! | ||
| 624 | case (TOP_INSTANCE(_, ht, _, _), _, name) | ||
| 625 | algorithm | ||
| 626 | // back one step in the instance hierarchy | ||
| 627 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : looking for: " + PrefixUtil.printPrefixStr(inPrefix) + "/" + name); | ||
| 628 | |||
| 629 | 491 | prefix := PrefixUtil.prefixStripLast(inPrefix); | |
| 630 | |||
| 631 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : stripping and looking for: " + PrefixUtil.printPrefixStr(prefix) + "/" + name); | ||
| 632 | |||
| 633 | // put the name as the last prefix | ||
| 634 | 491 | (_,cref) := PrefixUtil.prefixCref(FCore.emptyCache(), FGraph.empty(), emptyInstHierarchy, prefix, ComponentReferenceBasics.makeCrefIdent(name, DAE.T_UNKNOWN_DEFAULT, {})); | |
| 635 | |||
| 636 | // search in instance hierarchy we had a failure | ||
| 637 |
2/2✓ Branch 0 taken 491 times.
✓ Branch 1 taken 491 times.
|
982 | failure(get(cref, ht)); |
| 638 | |||
| 639 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : Couldn't find: " + ComponentReferenceBasics.printComponentRefStr(cref) + " going deeper"); | ||
| 640 | |||
| 641 | // call recursively to back one more step! | ||
| 642 | 491 | instInner := lookupInnerInIH(inTIH, prefix, name); | |
| 643 | then | ||
| 644 | instInner; | ||
| 645 | |||
| 646 | // if we fail return nothing | ||
| 647 | case (TOP_INSTANCE(), prefix, name) | ||
| 648 | algorithm | ||
| 649 | // fprintln(Flags.INNER_OUTER, "InnerOuter.lookupInnerInIH : looking for: " + PrefixUtil.printPrefixStr(prefix) + "/" + name + " NOT FOUND!"); | ||
| 650 | // dumpInstHierarchyHashTable(ht); | ||
| 651 | ✗ | then | |
| 652 | emptyInstInner(prefix, name); | ||
| 653 | end matchcontinue; | ||
| 654 | end lookupInnerInIH; | ||
| 655 | |||
| 656 | public function modificationOnOuter " | ||
| 657 | Author BZ, 2008-11 | ||
| 658 | According to specification modifiers on outer elements is not allowed." | ||
| 659 | input FCore.Cache cache; | ||
| 660 | input FCore.Graph env; | ||
| 661 | input InstHierarchy ih; | ||
| 662 | input DAE.Prefix prefix; | ||
| 663 | input String componentName; | ||
| 664 | input DAE.ComponentRef cr; | ||
| 665 | input DAE.Mod inMod; | ||
| 666 | input Absyn.InnerOuter io; | ||
| 667 | input Boolean impl; | ||
| 668 | input SourceInfo inInfo; | ||
| 669 | output Boolean modd; | ||
| 670 | algorithm | ||
| 671 | modd := matchcontinue(inMod, io) | ||
| 672 | local | ||
| 673 | String s1,s2,s; | ||
| 674 | // if we don't have the same modification on inner report error! | ||
| 675 | case(DAE.MOD(), Absyn.OUTER()) | ||
| 676 | algorithm | ||
| 677 | ✗ | s1 := ComponentReferenceBasics.printComponentRefStr(cr); | |
| 678 | ✗ | s2 := Mod.prettyPrintMod(inMod, 0); | |
| 679 | ✗ | s := s1 + " " + s2; | |
| 680 | ✗ | Error.addSourceMessage(Error.OUTER_MODIFICATION, {s}, inInfo); | |
| 681 | then | ||
| 682 | true; | ||
| 683 | |||
| 684 | else false; | ||
| 685 | |||
| 686 | end matchcontinue; | ||
| 687 | end modificationOnOuter; | ||
| 688 | |||
| 689 | public function switchInnerToOuterInGraph | ||
| 690 | "function switchInnerToOuterInGraph switches the inner to outer attributes of | ||
| 691 | a component in the Env." | ||
| 692 | input FCore.Graph inEnv; | ||
| 693 | input DAE.ComponentRef inCr; | ||
| 694 | output FCore.Graph outEnv; | ||
| 695 | algorithm | ||
| 696 | outEnv := match(inEnv,inCr) | ||
| 697 | local | ||
| 698 | DAE.ComponentRef cr; | ||
| 699 | FCore.Ref r; | ||
| 700 | FCore.Node n; | ||
| 701 | // handle nothingness | ||
| 702 | case (FCore.EG(_), _) then inEnv; | ||
| 703 | case (FCore.G(scope = {}), _) then inEnv; | ||
| 704 | // only need to handle top frame! | ||
| 705 | case (_, cr) | ||
| 706 | algorithm | ||
| 707 | 45 | r := FGraph.lastScopeRef(inEnv); | |
| 708 | 45 | n := FNode.fromRef(r); | |
| 709 | 45 | n := switchInnerToOuterInNode(n, cr); | |
| 710 | 45 | r := FNode.updateRef(r, n); | |
| 711 | then | ||
| 712 | inEnv; | ||
| 713 | end match; | ||
| 714 | end switchInnerToOuterInGraph; | ||
| 715 | |||
| 716 | protected function switchInnerToOuterInNode " | ||
| 717 | function switchInnerToOuterInFrame | ||
| 718 | switches the inner to outer attributes of a component in the Frame." | ||
| 719 | input FCore.Node inNode; | ||
| 720 | input DAE.ComponentRef inCr; | ||
| 721 | output FCore.Node outNode = inNode; | ||
| 722 | algorithm | ||
| 723 | () := match outNode | ||
| 724 | case FCore.N() | ||
| 725 | algorithm | ||
| 726 | 90 | outNode.children := FNode.RefTree.map(outNode.children, | |
| 727 | function switchInnerToOuterInChild(cr = inCr)); | ||
| 728 | then | ||
| 729 | (); | ||
| 730 | |||
| 731 | else (); | ||
| 732 | end match; | ||
| 733 | end switchInnerToOuterInNode; | ||
| 734 | |||
| 735 | protected function switchInnerToOuterInChild | ||
| 736 | input FCore.Name name; | ||
| 737 | input DAE.ComponentRef cr; | ||
| 738 | input FCore.Ref inRef; | ||
| 739 | output FCore.Ref ref; | ||
| 740 | protected | ||
| 741 | FCore.Node n; | ||
| 742 | algorithm | ||
| 743 | 1329 | n := FNode.fromRef(inRef); | |
| 744 | 1329 | n := switchInnerToOuterInChildrenValue(n, cr); | |
| 745 | 1329 | ref := FNode.updateRef(inRef, n); | |
| 746 | end switchInnerToOuterInChild; | ||
| 747 | |||
| 748 | protected function switchInnerToOuterInChildrenValue " | ||
| 749 | function switchInnerToOuterInChildrenValue | ||
| 750 | switches the inner to outer attributes of a component in the RefTree." | ||
| 751 | input FCore.Node inNode; | ||
| 752 | input DAE.ComponentRef inCr; | ||
| 753 | output FCore.Node outNode; | ||
| 754 | algorithm | ||
| 755 | outNode := matchcontinue inNode | ||
| 756 | local | ||
| 757 | FCore.Ref r; | ||
| 758 | FCore.Node node; | ||
| 759 | |||
| 760 | DAE.Ident name; | ||
| 761 | DAE.Attributes attributes; | ||
| 762 | SCode.Visibility visibility; | ||
| 763 | DAE.Type ty; | ||
| 764 | DAE.Binding binding; | ||
| 765 | Boolean bndsrc; | ||
| 766 | |||
| 767 | DAE.ConnectorType ct; | ||
| 768 | SCode.Parallelism parallelism "parallelism"; | ||
| 769 | SCode.Variability variability "variability" ; | ||
| 770 | Absyn.Direction direction "direction" ; | ||
| 771 | Option<DAE.Const> cnstForRange; | ||
| 772 | |||
| 773 | // inner | ||
| 774 | case node | ||
| 775 | algorithm | ||
| 776 | // get the instance child | ||
| 777 | 1329 | r := FNode.childFromNode(node, FNode.itNodeName); | |
| 778 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1311 times.
|
1311 | FCore.IT(DAE.TYPES_VAR(name, attributes, ty, binding, bndsrc, cnstForRange)) := FNode.refData(r); |
| 779 |
1/2✓ Branch 0 taken 1311 times.
✗ Branch 1 not taken.
|
1311 | DAE.ATTR(ct, parallelism, variability, direction, Absyn.INNER(), visibility) := attributes; |
| 780 | ✗ | attributes := DAE.ATTR(ct, parallelism, variability, direction, Absyn.OUTER(), visibility); | |
| 781 | // update the ref | ||
| 782 | ✗ | r := FNode.updateRef(r, FNode.setData(FNode.fromRef(r),FCore.IT(DAE.TYPES_VAR(name, attributes, ty, binding, bndsrc, cnstForRange)))); | |
| 783 | // env = switchInnerToOuterInGraph(env, inCr); | ||
| 784 | then | ||
| 785 | node; | ||
| 786 | |||
| 787 | // inner outer | ||
| 788 | case node | ||
| 789 | algorithm | ||
| 790 | // get the instance child | ||
| 791 | 1329 | r := FNode.childFromNode(node, FNode.itNodeName); | |
| 792 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1311 times.
|
1311 | FCore.IT(DAE.TYPES_VAR(name, attributes, ty, binding, bndsrc, cnstForRange)) := FNode.refData(r); |
| 793 |
1/2✓ Branch 0 taken 1311 times.
✗ Branch 1 not taken.
|
1311 | DAE.ATTR(ct, parallelism, variability, direction, Absyn.INNER_OUTER(), visibility) := attributes; |
| 794 | ✗ | attributes := DAE.ATTR(ct, parallelism, variability, direction, Absyn.OUTER(), visibility); | |
| 795 | // update the ref | ||
| 796 | ✗ | r := FNode.updateRef(r, FNode.setData(FNode.fromRef(r),FCore.IT(DAE.TYPES_VAR(name, attributes, ty, binding, bndsrc, cnstForRange)))); | |
| 797 | // env = switchInnerToOuterInGraph(env, inCr); | ||
| 798 | then | ||
| 799 | node; | ||
| 800 | |||
| 801 | // leave unchanged | ||
| 802 | case _ then inNode; | ||
| 803 | |||
| 804 | end matchcontinue; | ||
| 805 | end switchInnerToOuterInChildrenValue; | ||
| 806 | |||
| 807 | |||
| 808 | /////////////////////////////////////////////////// | ||
| 809 | /// instance hieararchy for inner/outer | ||
| 810 | /// add furher functions before this | ||
| 811 | /////////////////////////////////////////////////// | ||
| 812 | |||
| 813 | |||
| 814 | protected function emptyInstInner | ||
| 815 | input DAE.Prefix innerPrefix; | ||
| 816 | input String name; | ||
| 817 | output InstInner outInstInner; | ||
| 818 | annotation(__OpenModelica_EarlyInline = true); | ||
| 819 | algorithm | ||
| 820 | ✗ | outInstInner := INST_INNER(innerPrefix, name, Absyn.NOT_INNER_OUTER(), "", Absyn.IDENT(""), "", NONE(), {}, NONE()); | |
| 821 | end emptyInstInner; | ||
| 822 | |||
| 823 | public function lookupInnerVar | ||
| 824 | "@author: adrpo | ||
| 825 | This function lookups the result of instatiation of the inner | ||
| 826 | component given an instance hierarchy a prefix and a component name." | ||
| 827 | input Cache inCache; | ||
| 828 | input FCore.Graph inEnv; | ||
| 829 | input InstHierarchy inIH; | ||
| 830 | input DAE.Prefix inPrefix; | ||
| 831 | input SCode.Ident inIdent; | ||
| 832 | input Absyn.InnerOuter io; | ||
| 833 | output InstInner outInstInner; | ||
| 834 | algorithm | ||
| 835 | outInstInner := matchcontinue (inIH, inPrefix, inIdent) | ||
| 836 | local | ||
| 837 | String n; | ||
| 838 | DAE.Prefix pre; | ||
| 839 | TopInstance tih; | ||
| 840 | InstInner instInner; | ||
| 841 | |||
| 842 | // adrpo: if component is an outer or an inner/outer we need to | ||
| 843 | // lookup the modification of the inner component and use it | ||
| 844 | // when we instantiate the outer component | ||
| 845 | case (tih::_, pre, n) | ||
| 846 | algorithm | ||
| 847 | // is component an outer or an inner/outer? | ||
| 848 | //true = AbsynUtil.isOuter(io); // is outer | ||
| 849 | //false = AbsynUtil.isInner(io); // and is not inner | ||
| 850 | // search the instance hierarchy for the inner component | ||
| 851 | 699 | instInner := lookupInnerInIH(tih, pre, n); | |
| 852 | then | ||
| 853 | instInner; | ||
| 854 | |||
| 855 | // failure in case we look for anything else but outer! | ||
| 856 | case (_, pre, n) | ||
| 857 | algorithm | ||
| 858 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | true := Flags.isSet(Flags.FAILTRACE); |
| 859 | ✗ | Debug.traceln("InnerOuter.lookupInnerVar failed on component: " + PrefixUtil.printPrefixStr(pre) + "/" + n); | |
| 860 | ✗ | then | |
| 861 | fail(); | ||
| 862 | end matchcontinue; | ||
| 863 | end lookupInnerVar; | ||
| 864 | |||
| 865 | public function updateInstHierarchy | ||
| 866 | "@author: adrpo | ||
| 867 | This function updates the instance hierarchy by adding | ||
| 868 | the INNER components to it with the given prefix" | ||
| 869 | input InstHierarchy inIH; | ||
| 870 | input DAE.Prefix inPrefix; | ||
| 871 | input Absyn.InnerOuter inInnerOuter; | ||
| 872 | input InstInner inInstInner; | ||
| 873 | output InstHierarchy outIH; | ||
| 874 | algorithm | ||
| 875 | outIH := match(inIH, inInstInner) | ||
| 876 | local | ||
| 877 | TopInstance tih; | ||
| 878 | InstHierarchy restIH, ih; | ||
| 879 | DAE.ComponentRef cref; | ||
| 880 | SCode.Ident name; | ||
| 881 | InstHierarchyHashTable ht; | ||
| 882 | Option<Absyn.Path> pathOpt; | ||
| 883 | OuterPrefixes outerPrefixes; | ||
| 884 | DAE.ComponentRef cref_; | ||
| 885 | HashSet.HashSet sm; | ||
| 886 | |||
| 887 | /* only add inner elements | ||
| 888 | case(ih,inPrefix,inInnerOuter,inInstInner as INST_INNER(name=name)) | ||
| 889 | algorithm | ||
| 890 | false = AbsynUtil.isInner(inInnerOuter); | ||
| 891 | // prefix the name! | ||
| 892 | (_,cref) = PrefixUtil.prefixCref(FCore.emptyCache(),{},emptyInstHierarchy,inPrefix, ComponentReferenceBasics.makeCrefIdent(name, DAE.T_UNKNOWN_DEFAULT, {})); | ||
| 893 | // print ("InnerOuter.updateInstHierarchy jumping over non-inner: " + ComponentReferenceBasics.printComponentRefStr(cref) + "\n"); | ||
| 894 | then | ||
| 895 | ih;*/ | ||
| 896 | |||
| 897 | // no hashtable, create one! | ||
| 898 | case({}, INST_INNER()) | ||
| 899 | algorithm | ||
| 900 | // print ("InnerOuter.updateInstHierarchy creating an empty hash table! \n"); | ||
| 901 | 36 | ht := emptyInstHierarchyHashTable(); | |
| 902 | 36 | sm := HashSet.emptyHashSet(); | |
| 903 | 36 | tih := TOP_INSTANCE(NONE(), ht, emptyOuterPrefixes, sm); | |
| 904 | 36 | ih := updateInstHierarchy({tih}, inPrefix, inInnerOuter, inInstInner); | |
| 905 | then | ||
| 906 | ih; | ||
| 907 | |||
| 908 | // add to the hierarchy | ||
| 909 | case((TOP_INSTANCE(pathOpt, ht, outerPrefixes, sm))::restIH, INST_INNER(name=name)) | ||
| 910 | algorithm | ||
| 911 | // prefix the name! | ||
| 912 | 738 | cref_ := ComponentReferenceBasics.makeCrefIdent(name, DAE.T_UNKNOWN_DEFAULT, {}); | |
| 913 | 738 | (_,cref) := PrefixUtil.prefixCref(FCore.emptyCache(), FGraph.empty(), emptyInstHierarchy, inPrefix, cref_); | |
| 914 | // add to hashtable! | ||
| 915 | // fprintln(Flags.FAILTRACE, "InnerOuter.updateInstHierarchy adding: " + PrefixUtil.printPrefixStr(inPrefix) + "/" + name + " to IH"); | ||
| 916 | 738 | ht := add((cref,inInstInner), ht); | |
| 917 | 738 | then | |
| 918 | TOP_INSTANCE(pathOpt, ht, outerPrefixes, sm)::restIH; | ||
| 919 | |||
| 920 | // failure | ||
| 921 | case(_, INST_INNER()) | ||
| 922 | algorithm | ||
| 923 | // prefix the name! | ||
| 924 | //(_,cref) = PrefixUtil.prefixCref(FCore.emptyCache(),{},emptyInstHierarchy,inPrefix, ComponentReferenceBasics.makeCrefIdent("UNKNOWN", DAE.T_UNKNOWN_DEFAULT, {})); | ||
| 925 | // fprintln(Flags.INNER_OUTER, "InnerOuter.updateInstHierarchy failure for: " + | ||
| 926 | // PrefixUtil.printPrefixStr(inPrefix) + "/" + name); | ||
| 927 | then | ||
| 928 | fail(); | ||
| 929 | end match; | ||
| 930 | end updateInstHierarchy; | ||
| 931 | |||
| 932 | public function updateSMHierarchy | ||
| 933 | "@author: BTH | ||
| 934 | Add State Machine state to collection of State Machine states in instance hierarchy." | ||
| 935 | input DAE.ComponentRef smState; | ||
| 936 | input InstHierarchy inIH; | ||
| 937 | output InstHierarchy outIH; | ||
| 938 | algorithm | ||
| 939 | outIH := match (smState, inIH) | ||
| 940 | local | ||
| 941 | TopInstance tih; | ||
| 942 | InstHierarchy restIH, ih; | ||
| 943 | DAE.ComponentRef cref; | ||
| 944 | SCode.Ident name; | ||
| 945 | InstHierarchyHashTable ht; | ||
| 946 | Option<Absyn.Path> pathOpt; | ||
| 947 | OuterPrefixes outerPrefixes; | ||
| 948 | HashSet.HashSet sm; | ||
| 949 | HashSet.HashSet sm2; | ||
| 950 | |||
| 951 | // no hashtable, create one! | ||
| 952 | case(_,{}) | ||
| 953 | algorithm | ||
| 954 | 4 | ht := emptyInstHierarchyHashTable(); | |
| 955 | 4 | sm := HashSet.emptyHashSet(); | |
| 956 | //sm = Debug.bcallret2(true, BaseHashSet.add, smState, sm, sm); | ||
| 957 | 4 | sm2 := BaseHashSet.add(smState, sm); | |
| 958 | // FIXME what to put for emptyOuterPrefixes | ||
| 959 | 4 | tih := TOP_INSTANCE(NONE(), ht, emptyOuterPrefixes, sm2); | |
| 960 | |||
| 961 | ih := {tih}; | ||
| 962 | then | ||
| 963 | ih; | ||
| 964 | |||
| 965 | // add to the hierarchy | ||
| 966 | case (cref,TOP_INSTANCE(pathOpt, ht, outerPrefixes, sm)::restIH) | ||
| 967 | algorithm | ||
| 968 | // add to hashtable! | ||
| 969 | 36 | sm := BaseHashSet.add(cref, sm); // add((cref,inInstInner), ht); | |
| 970 | 36 | then | |
| 971 | TOP_INSTANCE(pathOpt, ht, outerPrefixes, sm)::restIH; | ||
| 972 | |||
| 973 | // failure | ||
| 974 | case (DAE.CREF_IDENT(ident=name),_) | ||
| 975 | algorithm | ||
| 976 | ✗ | true := Flags.isSet(Flags.INSTANCE); | |
| 977 | ✗ | Debug.traceln("InnerOuter.updateSMHierarchy failure for: " + name); | |
| 978 | ✗ | then | |
| 979 | fail(); | ||
| 980 | end match; | ||
| 981 | end updateSMHierarchy; | ||
| 982 | |||
| 983 | |||
| 984 | public function addClassIfInner | ||
| 985 | input SCode.Element inClass; | ||
| 986 | input DAE.Prefix inPrefix; | ||
| 987 | input FCore.Graph inScope; | ||
| 988 | input InstHierarchy inIH; | ||
| 989 | output InstHierarchy outIH; | ||
| 990 | algorithm | ||
| 991 | outIH := matchcontinue inClass | ||
| 992 | local | ||
| 993 | String name, scopeName; | ||
| 994 | Absyn.InnerOuter io; | ||
| 995 | |||
| 996 | // add inner or innerouter | ||
| 997 | case SCode.CLASS(name = name, prefixes = SCode.PREFIXES(innerOuter = io)) | ||
| 998 | algorithm | ||
| 999 |
1/2✓ Branch 1 taken 8064891 times.
✗ Branch 2 not taken.
|
8064891 | true := AbsynUtil.isInner(io); |
| 1000 | ✗ | scopeName := FGraph.getGraphNameStr(inScope); | |
| 1001 | // add to instance hierarchy | ||
| 1002 | ✗ | outIH := updateInstHierarchy(inIH, inPrefix, io, | |
| 1003 | INST_INNER( | ||
| 1004 | inPrefix, // prefix | ||
| 1005 | name, // class name | ||
| 1006 | io, | ||
| 1007 | name, | ||
| 1008 | Absyn.IDENT(name), | ||
| 1009 | scopeName, | ||
| 1010 | NONE(), | ||
| 1011 | {}, | ||
| 1012 | SOME(inClass))); | ||
| 1013 | then | ||
| 1014 | outIH; | ||
| 1015 | |||
| 1016 | // do nothing if not inner | ||
| 1017 | else inIH; | ||
| 1018 | |||
| 1019 | end matchcontinue; | ||
| 1020 | end addClassIfInner; | ||
| 1021 | |||
| 1022 | public function addOuterPrefixToIH | ||
| 1023 | "@author: adrpo | ||
| 1024 | This function remembers the outer prefix with the correct prefix of the inner" | ||
| 1025 | input InstHierarchy inIH; | ||
| 1026 | input DAE.ComponentRef inOuterComponentRef; | ||
| 1027 | input DAE.ComponentRef inInnerComponentRef; | ||
| 1028 | output InstHierarchy outIH; | ||
| 1029 | algorithm | ||
| 1030 | outIH := matchcontinue inIH | ||
| 1031 | local | ||
| 1032 | TopInstance tih; | ||
| 1033 | InstHierarchy restIH, ih; | ||
| 1034 | InstHierarchyHashTable ht; | ||
| 1035 | Option<Absyn.Path> pathOpt; | ||
| 1036 | OuterPrefixes outerPrefixes; | ||
| 1037 | HashSet.HashSet sm; | ||
| 1038 | |||
| 1039 | // no hashtable, create one! | ||
| 1040 | case {} | ||
| 1041 | algorithm | ||
| 1042 | // create an empty table and add the crefs to it. | ||
| 1043 | ✗ | ht := emptyInstHierarchyHashTable(); | |
| 1044 | ✗ | sm := HashSet.emptyHashSet(); | |
| 1045 | ✗ | tih := TOP_INSTANCE(NONE(), ht, {OUTER(ComponentReference.crefStripSubs(inOuterComponentRef), inInnerComponentRef)}, sm); | |
| 1046 | ih := {tih}; | ||
| 1047 | then | ||
| 1048 | ih; | ||
| 1049 | |||
| 1050 | // add to the top instance | ||
| 1051 | case (TOP_INSTANCE(pathOpt, ht, outerPrefixes, sm))::restIH | ||
| 1052 | algorithm | ||
| 1053 | // fprintln(Flags.INNER_OUTER, "InnerOuter.addOuterPrefix adding: outer cref: " + | ||
| 1054 | // ComponentReferenceBasics.printComponentRefStr(inOuterComponentRef) + " refers to inner cref: " + | ||
| 1055 | // ComponentReferenceBasics.printComponentRefStr(inInnerComponentRef) + " to IH"); | ||
| 1056 | 693 | outerPrefixes := List.unionElt(OUTER(ComponentReference.crefStripSubs(inOuterComponentRef), inInnerComponentRef), outerPrefixes); | |
| 1057 | 693 | then | |
| 1058 | TOP_INSTANCE(pathOpt, ht, outerPrefixes, sm)::restIH; | ||
| 1059 | |||
| 1060 | // failure | ||
| 1061 | else | ||
| 1062 | algorithm | ||
| 1063 | ✗ | true := Flags.isSet(Flags.FAILTRACE); | |
| 1064 | ✗ | Debug.traceln("InnerOuter.addOuterPrefix failed to add: outer cref: " + | |
| 1065 | ComponentReferenceBasics.printComponentRefStr(inOuterComponentRef) + " refers to inner cref: " + | ||
| 1066 | ComponentReferenceBasics.printComponentRefStr(inInnerComponentRef) + " to IH"); | ||
| 1067 | ✗ | then | |
| 1068 | fail(); | ||
| 1069 | end matchcontinue; | ||
| 1070 | end addOuterPrefixToIH; | ||
| 1071 | |||
| 1072 | public function prefixOuterCrefWithTheInnerPrefix | ||
| 1073 | "@author: adrpo | ||
| 1074 | This function searches for outer crefs and prefixes them with the inner prefix" | ||
| 1075 | input InstHierarchy inIH; | ||
| 1076 | input DAE.ComponentRef inOuterComponentRef; | ||
| 1077 | input DAE.Prefix inPrefix; | ||
| 1078 | output DAE.ComponentRef outInnerComponentRef; | ||
| 1079 | algorithm | ||
| 1080 | outInnerComponentRef := match inIH | ||
| 1081 | local | ||
| 1082 | DAE.ComponentRef outerCrefPrefix, fullCref, innerCref, innerCrefPrefix; | ||
| 1083 | OuterPrefixes outerPrefixes; | ||
| 1084 | |||
| 1085 | // we have no outer references, fail so prefixing can happen in the calling function | ||
| 1086 | case {} | ||
| 1087 | 32908 | then | |
| 1088 | fail(); | ||
| 1089 | |||
| 1090 | // we have some outer references, search for our prefix + cref in them | ||
| 1091 | case {TOP_INSTANCE(_, _, outerPrefixes as _::_, _)} | ||
| 1092 | algorithm | ||
| 1093 | 104474 | (_,fullCref) := PrefixUtil.prefixCref(FCore.emptyCache(), FGraph.empty(), emptyInstHierarchy, inPrefix, inOuterComponentRef); | |
| 1094 | |||
| 1095 | // this will fail if we don't find it so prefixing can happen in the calling function | ||
| 1096 | 104474 | (outerCrefPrefix, innerCrefPrefix) := searchForInnerPrefix(fullCref, inOuterComponentRef, outerPrefixes); | |
| 1097 | |||
| 1098 | 1625 | innerCref := changeOuterReferenceToInnerReference(fullCref, outerCrefPrefix, innerCrefPrefix); | |
| 1099 | |||
| 1100 | // fprintln(Flags.FAILTRACE, "- InnerOuter.prefixOuterCrefWithTheInnerPrefix replaced cref " + ComponentReferenceBasics.printComponentRefStr(fullCref) + " with cref: " + ComponentReferenceBasics.printComponentRefStr(innerCref)); | ||
| 1101 | then | ||
| 1102 | innerCref; | ||
| 1103 | |||
| 1104 | // failure | ||
| 1105 | else | ||
| 1106 | algorithm | ||
| 1107 | // true = Flags.isSet(Flags.FAILTRACE); | ||
| 1108 | // Debug.traceln("- InnerOuter.prefixOuterCrefWithTheInnerPrefix failed to find prefix of inner for outer: prefix/cref " + PrefixUtil.printPrefixStr(inPrefix) + "/" + ComponentReferenceBasics.printComponentRefStr(inOuterComponentRef)); | ||
| 1109 | then | ||
| 1110 | fail(); | ||
| 1111 | end match; | ||
| 1112 | end prefixOuterCrefWithTheInnerPrefix; | ||
| 1113 | |||
| 1114 | protected function changeOuterReferenceToInnerReference | ||
| 1115 | "@author: adrpo | ||
| 1116 | This function replaces the outer prefix with the inner prefix in the full cref" | ||
| 1117 | input DAE.ComponentRef inFullCref; | ||
| 1118 | input DAE.ComponentRef inOuterCrefPrefix; | ||
| 1119 | input DAE.ComponentRef inInnerCrefPrefix; | ||
| 1120 | output DAE.ComponentRef outInnerCref; | ||
| 1121 | algorithm | ||
| 1122 | outInnerCref := match(inFullCref, inOuterCrefPrefix, inInnerCrefPrefix) | ||
| 1123 | local | ||
| 1124 | DAE.ComponentRef ifull, ocp, icp, ic; | ||
| 1125 | list<DAE.ComponentRef> eifull, eocp, eicp, epre, erest, esuffix; | ||
| 1126 | |||
| 1127 | // The full cref might contain subscripts that we wish to keep, so we use | ||
| 1128 | // the inner and outer prefix to extract the relevant parts of the full cref. | ||
| 1129 | // | ||
| 1130 | // E.g. if we have a full cref a.b.c.d.e.f.g, an outer prefix a.b.c.d.e and | ||
| 1131 | // an inner prefix a.d.e, then we want a, d.e and f.g, resulting in a.d.e.f.g. | ||
| 1132 | case (ifull, ocp, icp) | ||
| 1133 | algorithm | ||
| 1134 | // print("F:" + ComponentReferenceBasics.printComponentRefStr(ifull) + "\n" + "I:" + ComponentReferenceBasics.printComponentRefStr(icp) + "\n" + "O:" + ComponentReferenceBasics.printComponentRefStr(ocp) + "\n"); | ||
| 1135 | // Explode the crefs to lists so that they are easier to work with. | ||
| 1136 | 1625 | eifull := ComponentReference.explode(ifull); | |
| 1137 | 1625 | eicp := ComponentReference.explode(icp); | |
| 1138 | |||
| 1139 | // Split the full cref so that we get the part that is equal to the | ||
| 1140 | // outer prefix and the rest of the suffix. | ||
| 1141 | 1625 | (eocp, esuffix) := List.split(eifull, ComponentReference.identifierCount(ocp)); | |
| 1142 | |||
| 1143 | // Extract the common prefix of the outer and inner prefix. | ||
| 1144 | 1625 | (epre, erest) := List.splitEqualPrefix(eocp, eicp, ComponentReferenceBasics.crefFirstIdentEqual); | |
| 1145 | |||
| 1146 | // remove the common prefix from the inner! | ||
| 1147 | 1625 | (_, eicp) := List.splitEqualPrefix(eicp, epre, ComponentReferenceBasics.crefFirstIdentEqual); | |
| 1148 | |||
| 1149 | // Extract the common suffix of the outer and inner prefix. | ||
| 1150 | 1625 | (erest, _) := List.splitEqualPrefix(listReverse(erest), listReverse(eicp), ComponentReferenceBasics.crefFirstIdentEqual); | |
| 1151 | |||
| 1152 | // Combine the parts into a new cref. | ||
| 1153 | 1625 | erest := List.append_reverse(erest, esuffix); | |
| 1154 | 1625 | eifull := listAppend(epre, erest); | |
| 1155 | 1625 | ic := ComponentReference.implode(eifull); | |
| 1156 | // print("C:" + ComponentReferenceBasics.printComponentRefStr(ic) + "\n"); | ||
| 1157 | then | ||
| 1158 | ic; | ||
| 1159 | |||
| 1160 | end match; | ||
| 1161 | end changeOuterReferenceToInnerReference; | ||
| 1162 | |||
| 1163 | protected function searchForInnerPrefix | ||
| 1164 | "@author: adrpo | ||
| 1165 | search in the outer prefixes and retrieve the outer/inner crefs" | ||
| 1166 | input DAE.ComponentRef fullCref; | ||
| 1167 | input DAE.ComponentRef inOuterCref; | ||
| 1168 | input OuterPrefixes outerPrefixes; | ||
| 1169 | output DAE.ComponentRef outerCrefPrefix; | ||
| 1170 | output DAE.ComponentRef innerCrefPrefix; | ||
| 1171 | protected | ||
| 1172 | DAE.ComponentRef cr; | ||
| 1173 | Boolean b1 = false, b2 = false; | ||
| 1174 | algorithm | ||
| 1175 | // print("L:" + intString(listLength(outerPrefixes)) + "\n"); | ||
| 1176 |
2/2✓ Branch 0 taken 4402740 times.
✓ Branch 1 taken 102849 times.
|
4505589 | for op in outerPrefixes loop |
| 1177 | 4402740 | OUTER(outerComponentRef = outerCrefPrefix) := op; | |
| 1178 | 4402740 | b1 := ComponentReferenceBasics.crefPrefixOfIgnoreSubscripts(outerCrefPrefix, fullCref); | |
| 1179 |
2/2✓ Branch 0 taken 4401115 times.
✓ Branch 1 taken 1625 times.
|
4402740 | if not b1 |
| 1180 | then | ||
| 1181 | 4401115 | cr := ComponentReference.crefStripLastIdent(outerCrefPrefix); | |
| 1182 |
5/6✓ Branch 2 taken 291082 times.
✓ Branch 3 taken 4110033 times.
✓ Branch 7 taken 720 times.
✓ Branch 8 taken 290362 times.
✓ Branch 10 taken 720 times.
✗ Branch 11 not taken.
|
4401115 | b2 := ComponentReferenceBasics.crefLastIdent(outerCrefPrefix) == ComponentReferenceBasics.crefFirstIdent(inOuterCref) |
| 1183 | and ComponentReferenceBasics.crefPrefixOfIgnoreSubscripts(cr, fullCref); | ||
| 1184 | end if; | ||
| 1185 | |||
| 1186 |
2/2✓ Branch 0 taken 1625 times.
✓ Branch 1 taken 4401115 times.
|
4402740 | if b1 or b2 |
| 1187 | then | ||
| 1188 | 1625 | OUTER(innerComponentRef = innerCrefPrefix) := op; | |
| 1189 |
1/2✓ Branch 0 taken 1625 times.
✗ Branch 1 not taken.
|
1625 | return; |
| 1190 | end if; | ||
| 1191 | end for; | ||
| 1192 | |||
| 1193 | 102849 | fail(); | |
| 1194 | end searchForInnerPrefix; | ||
| 1195 | |||
| 1196 | protected function printInnerDefStr | ||
| 1197 | input InstInner inInstInner; | ||
| 1198 | output String outStr; | ||
| 1199 | algorithm | ||
| 1200 | outStr := match inInstInner | ||
| 1201 | local | ||
| 1202 | String fullName "full inner component name"; | ||
| 1203 | Absyn.Path typePath "the type of the inner"; | ||
| 1204 | String scope "the scope of the inner"; | ||
| 1205 | list<DAE.ComponentRef> outers "which outers are referencing this inner"; | ||
| 1206 | String str, strOuters; | ||
| 1207 | |||
| 1208 | case INST_INNER(_, _, _, fullName, typePath, scope, _, outers, _) | ||
| 1209 | algorithm | ||
| 1210 | ✗ | outers := List.uniqueOnTrue(outers, ComponentReferenceBasics.crefEqualNoStringCompare); | |
| 1211 | ✗ | strOuters := if listEmpty(outers) | |
| 1212 | then "" | ||
| 1213 | else (" Referenced by 'outer' components: {" + | ||
| 1214 | stringDelimitList(List.map(outers, ComponentReferenceBasics.printComponentRefStr), ", ") + "}"); | ||
| 1215 | ✗ | str := AbsynUtil.pathString(typePath) + " " + fullName + "; defined in scope: " + scope + "." + strOuters; | |
| 1216 | then | ||
| 1217 | str; | ||
| 1218 | end match; | ||
| 1219 | end printInnerDefStr; | ||
| 1220 | |||
| 1221 | public function getExistingInnerDeclarations | ||
| 1222 | "@author: adrpo | ||
| 1223 | This function retrieves all the existing inner declarations as a string" | ||
| 1224 | input InstHierarchy inIH; | ||
| 1225 | input FCore.Graph inEnv; | ||
| 1226 | output String innerDeclarations; | ||
| 1227 | algorithm | ||
| 1228 | innerDeclarations := match inIH | ||
| 1229 | local | ||
| 1230 | InstHierarchyHashTable ht; | ||
| 1231 | list<InstInner> inners; | ||
| 1232 | String str; | ||
| 1233 | |||
| 1234 | // we have no inner components yet | ||
| 1235 | case {} | ||
| 1236 | 2 | then | |
| 1237 | "There are no 'inner' components defined in the model in any of the parent scopes of 'outer' component's scope: " + FGraph.printGraphPathStr(inEnv) + "." ; | ||
| 1238 | |||
| 1239 | // get the list of components | ||
| 1240 | case (TOP_INSTANCE(_, ht, _, _))::_ | ||
| 1241 | algorithm | ||
| 1242 | ✗ | inners := getInnersFromInstHierarchyHashTable(ht); | |
| 1243 | ✗ | str := stringDelimitList(List.map(inners, printInnerDefStr), "\n "); | |
| 1244 | then | ||
| 1245 | str; | ||
| 1246 | end match; | ||
| 1247 | end getExistingInnerDeclarations; | ||
| 1248 | |||
| 1249 | protected function getInnersFromInstHierarchyHashTable | ||
| 1250 | "@author: adrpo | ||
| 1251 | Returns all the inners defined in the hashtable." | ||
| 1252 | input InstHierarchyHashTable t; | ||
| 1253 | output list<InstInner> inners; | ||
| 1254 | algorithm | ||
| 1255 | ✗ | inners := List.map(hashTableList(t),getValue); | |
| 1256 | end getInnersFromInstHierarchyHashTable; | ||
| 1257 | |||
| 1258 | protected function getValue | ||
| 1259 | input tuple<Key,Value> tpl; | ||
| 1260 | output InstInner v; | ||
| 1261 | algorithm | ||
| 1262 | v := match tpl | ||
| 1263 | case (_,v) then v; | ||
| 1264 | end match; | ||
| 1265 | end getValue; | ||
| 1266 | |||
| 1267 | ///////////////////////////////////////////////////////////////// | ||
| 1268 | // hash table implementation for InnerOuter instance hierarchy // | ||
| 1269 | ///////////////////////////////////////////////////////////////// | ||
| 1270 | |||
| 1271 | protected function hashFunc | ||
| 1272 | "author: PA | ||
| 1273 | Calculates a hash value for DAE.ComponentRef" | ||
| 1274 | input Key k; | ||
| 1275 | output Integer res; | ||
| 1276 | algorithm | ||
| 1277 | 3164 | res := stringHashDjb2(ComponentReferenceBasics.printComponentRefStr(k)); | |
| 1278 | end hashFunc; | ||
| 1279 | |||
| 1280 | protected function keyEqual | ||
| 1281 | input Key key1; | ||
| 1282 | input Key key2; | ||
| 1283 | output Boolean res; | ||
| 1284 | algorithm | ||
| 1285 | 4188 | res := ComponentReferenceBasics.crefEqualNoStringCompare(key1,key2); | |
| 1286 | end keyEqual; | ||
| 1287 | |||
| 1288 | protected function dumpInstHierarchyHashTable "" | ||
| 1289 | input InstHierarchyHashTable t; | ||
| 1290 | algorithm | ||
| 1291 | ✗ | print("InstHierarchyHashTable:\n"); | |
| 1292 | ✗ | print(stringDelimitList(List.map(hashTableList(t),dumpTuple),"\n")); | |
| 1293 | ✗ | print("\n"); | |
| 1294 | end dumpInstHierarchyHashTable; | ||
| 1295 | |||
| 1296 | protected function dumpTuple | ||
| 1297 | input tuple<Key,Value> tpl; | ||
| 1298 | output String str; | ||
| 1299 | algorithm | ||
| 1300 | str := match tpl | ||
| 1301 | local | ||
| 1302 | Key k; case (k,_) | ||
| 1303 | algorithm | ||
| 1304 | ✗ | str := "{" + | |
| 1305 | ComponentReference.crefStr(k) + | ||
| 1306 | " opaque InstInner for now, implement printing. " + "}\n"; | ||
| 1307 | then str; | ||
| 1308 | end match; | ||
| 1309 | end dumpTuple; | ||
| 1310 | |||
| 1311 | /* end of InstHierarchyHashTable instance specific code */ | ||
| 1312 | |||
| 1313 | /* Generic hashtable code below!! */ | ||
| 1314 | public | ||
| 1315 | uniontype InstHierarchyHashTable | ||
| 1316 | record HASHTABLE | ||
| 1317 | array<list<tuple<Key,Integer>>> hashTable " hashtable to translate Key to array indx" ; | ||
| 1318 | ValueArray valueArr "Array of values" ; | ||
| 1319 | Integer bucketSize "bucket size" ; | ||
| 1320 | Integer numberOfEntries "number of entries in hashtable" ; | ||
| 1321 | end HASHTABLE; | ||
| 1322 | end InstHierarchyHashTable; | ||
| 1323 | |||
| 1324 | uniontype ValueArray | ||
| 1325 | "array of values are expandable, to amortize the | ||
| 1326 | cost of adding elements in a more efficient manner" | ||
| 1327 | record VALUE_ARRAY | ||
| 1328 | Integer numberOfElements "number of elements in hashtable" ; | ||
| 1329 | array<Option<tuple<Key,Value>>> valueArray "array of values"; | ||
| 1330 | end VALUE_ARRAY; | ||
| 1331 | end ValueArray; | ||
| 1332 | |||
| 1333 | protected function emptyInstHierarchyHashTable | ||
| 1334 | "author: PA | ||
| 1335 | Returns an empty InstHierarchyHashTable. | ||
| 1336 | Using the bucketsize 100 and array size 10." | ||
| 1337 | output InstHierarchyHashTable hashTable; | ||
| 1338 | protected | ||
| 1339 | array<list<tuple<Key,Integer>>> arr; | ||
| 1340 | array<Option<tuple<Key,Value>>> emptyarr; | ||
| 1341 | algorithm | ||
| 1342 | 40 | arr := arrayCreate(1000, {}); | |
| 1343 | 40 | emptyarr := arrayCreate(100, NONE()); | |
| 1344 | 40 | hashTable := HASHTABLE(arr,VALUE_ARRAY(0,emptyarr),1000,0); | |
| 1345 | end emptyInstHierarchyHashTable; | ||
| 1346 | |||
| 1347 | protected function add | ||
| 1348 | "author: PA | ||
| 1349 | Add a Key-Value tuple to hashtable. | ||
| 1350 | If the Key-Value tuple already exists, the function updates the Value." | ||
| 1351 | input tuple<Key,Value> entry; | ||
| 1352 | input InstHierarchyHashTable hashTable; | ||
| 1353 | output InstHierarchyHashTable outHashTable; | ||
| 1354 | algorithm | ||
| 1355 | outHashTable := | ||
| 1356 | matchcontinue (entry,hashTable) | ||
| 1357 | local | ||
| 1358 | Integer hval,indx,newpos,n,n_1,bsize; | ||
| 1359 | ValueArray varr_1,varr; | ||
| 1360 | list<tuple<Key,Integer>> indexes; | ||
| 1361 | array<list<tuple<Key,Integer>>> hashvec_1,hashvec; | ||
| 1362 | tuple<Key,Value> v,newv; | ||
| 1363 | Key key; | ||
| 1364 | /* Adding when not existing previously */ | ||
| 1365 | case ((v as (key,_)),(HASHTABLE(hashvec,varr,bsize,_))) | ||
| 1366 | algorithm | ||
| 1367 |
2/2✓ Branch 0 taken 738 times.
✓ Branch 1 taken 44 times.
|
782 | failure(get(key, hashTable)); |
| 1368 | 44 | hval := hashFunc(key); | |
| 1369 | 44 | indx := intMod(hval, bsize); | |
| 1370 | 44 | newpos := valueArrayLength(varr); | |
| 1371 | 44 | varr_1 := valueArrayAdd(varr, v); | |
| 1372 | 44 | indexes := hashvec[indx + 1]; | |
| 1373 | 88 | hashvec_1 := arrayUpdate(hashvec, indx + 1, ((key,newpos) :: indexes)); | |
| 1374 | 44 | n_1 := valueArrayLength(varr_1); | |
| 1375 | // print("Added NEW to IH: key:" + ComponentReferenceBasics.printComponentRefStr(key) + " value: " + printInnerDefStr(value) + "\n"); | ||
| 1376 | 44 | then HASHTABLE(hashvec_1,varr_1,bsize,n_1); | |
| 1377 | |||
| 1378 | /* adding when already present => Updating value */ | ||
| 1379 | case ((newv as (key,_)),(HASHTABLE(hashvec,varr,bsize,n))) | ||
| 1380 | algorithm | ||
| 1381 | 694 | (_,indx) := get1(key, hashTable); | |
| 1382 | //print("adding when present, indx =" );print(intString(indx));print("\n"); | ||
| 1383 | 694 | varr_1 := valueArraySetnth(varr, indx, newv); | |
| 1384 | // print("Updated NEW to IH: key:" + ComponentReferenceBasics.printComponentRefStr(key) + " value: " + printInnerDefStr(value) + "\n"); | ||
| 1385 | 694 | then HASHTABLE(hashvec,varr_1,bsize,n); | |
| 1386 | else | ||
| 1387 | algorithm | ||
| 1388 | ✗ | print("- InnerOuter.add failed\n"); | |
| 1389 | ✗ | then | |
| 1390 | fail(); | ||
| 1391 | end matchcontinue; | ||
| 1392 | end add; | ||
| 1393 | |||
| 1394 | public function get | ||
| 1395 | "author: PA | ||
| 1396 | Returns a Value given a Key and a InstHierarchyHashTable." | ||
| 1397 | input Key key; | ||
| 1398 | input InstHierarchyHashTable hashTable; | ||
| 1399 | output Value value; | ||
| 1400 | algorithm | ||
| 1401 | 2426 | (value,_):= get1(key,hashTable); | |
| 1402 | end get; | ||
| 1403 | |||
| 1404 | protected function get1 "help function to get" | ||
| 1405 | input Key key; | ||
| 1406 | input InstHierarchyHashTable hashTable; | ||
| 1407 | output Value value; | ||
| 1408 | output Integer indx; | ||
| 1409 | algorithm | ||
| 1410 | (value, indx) := match hashTable | ||
| 1411 | local | ||
| 1412 | Integer hval,hashindx,bsize; | ||
| 1413 | list<tuple<Key,Integer>> indexes; | ||
| 1414 | Value v; | ||
| 1415 | array<list<tuple<Key,Integer>>> hashvec; | ||
| 1416 | ValueArray varr; | ||
| 1417 | Key k; | ||
| 1418 | |||
| 1419 | case HASHTABLE(hashvec,varr,bsize,_) | ||
| 1420 | algorithm | ||
| 1421 | 3120 | hval := hashFunc(key); | |
| 1422 | hashindx := intMod(hval, bsize); | ||
| 1423 | 3120 | indexes := hashvec[hashindx + 1]; | |
| 1424 | 3120 | indx := get2(key, indexes); | |
| 1425 | 2094 | (k, v) := valueArrayNth(varr, indx); | |
| 1426 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2094 times.
|
2094 | true := keyEqual(k, key); |
| 1427 |
2/2✓ Branch 0 taken 694 times.
✓ Branch 1 taken 1400 times.
|
2094 | then |
| 1428 | (v,indx); | ||
| 1429 | end match; | ||
| 1430 | end get1; | ||
| 1431 | |||
| 1432 | protected function get2 | ||
| 1433 | "author: PA | ||
| 1434 | Helper function to get" | ||
| 1435 | input Key key; | ||
| 1436 | input list<tuple<Key,Integer>> keyIndices; | ||
| 1437 | output Integer index; | ||
| 1438 | algorithm | ||
| 1439 | index := matchcontinue keyIndices | ||
| 1440 | local | ||
| 1441 | Key key2; | ||
| 1442 | list<tuple<Key,Integer>> xs; | ||
| 1443 | case (key2,index) :: _ | ||
| 1444 | algorithm | ||
| 1445 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2094 times.
|
2094 | true := keyEqual(key, key2); |
| 1446 | then | ||
| 1447 | index; | ||
| 1448 | case _ :: xs | ||
| 1449 | algorithm | ||
| 1450 | ✗ | index := get2(key, xs); | |
| 1451 | then | ||
| 1452 | index; | ||
| 1453 | end matchcontinue; | ||
| 1454 | end get2; | ||
| 1455 | |||
| 1456 | protected function hashTableList "returns the entries in the hashTable as a list of tuple<Key,Value>" | ||
| 1457 | input InstHierarchyHashTable hashTable; | ||
| 1458 | output list<tuple<Key,Value>> tplLst; | ||
| 1459 | algorithm | ||
| 1460 | tplLst := match hashTable | ||
| 1461 | local ValueArray varr; | ||
| 1462 | case HASHTABLE(valueArr = varr) algorithm | ||
| 1463 | ✗ | tplLst := valueArrayList(varr); | |
| 1464 | then tplLst; | ||
| 1465 | end match; | ||
| 1466 | end hashTableList; | ||
| 1467 | |||
| 1468 | protected function valueArrayList | ||
| 1469 | "author: PA | ||
| 1470 | Transforms a ValueArray to a tuple<Key,Value> list" | ||
| 1471 | input ValueArray valueArray; | ||
| 1472 | output list<tuple<Key,Value>> tplLst; | ||
| 1473 | algorithm | ||
| 1474 | tplLst := matchcontinue valueArray | ||
| 1475 | local | ||
| 1476 | array<Option<tuple<Key,Value>>> arr; | ||
| 1477 | tuple<Key,Value> elt; | ||
| 1478 | Integer lastpos,n; | ||
| 1479 | list<tuple<Key,Value>> lst; | ||
| 1480 | case VALUE_ARRAY(numberOfElements = 0) then {}; | ||
| 1481 | case VALUE_ARRAY(numberOfElements = 1,valueArray = arr) | ||
| 1482 | algorithm | ||
| 1483 | ✗ | SOME(elt) := arr[0 + 1]; | |
| 1484 | then | ||
| 1485 | {elt}; | ||
| 1486 | case VALUE_ARRAY(numberOfElements = n,valueArray = arr) | ||
| 1487 | algorithm | ||
| 1488 | ✗ | lastpos := n - 1; | |
| 1489 | ✗ | lst := valueArrayList2(arr, 0, lastpos); | |
| 1490 | then | ||
| 1491 | lst; | ||
| 1492 | end matchcontinue; | ||
| 1493 | end valueArrayList; | ||
| 1494 | |||
| 1495 | protected function valueArrayList2 "Helper function to valueArrayList" | ||
| 1496 | input array<Option<tuple<Key,Value>>> inVarOptionArray1; | ||
| 1497 | input Integer inInteger2; | ||
| 1498 | input Integer inInteger3; | ||
| 1499 | output list<tuple<Key,Value>> outVarLst; | ||
| 1500 | algorithm | ||
| 1501 | outVarLst := matchcontinue (inVarOptionArray1,inInteger2,inInteger3) | ||
| 1502 | local | ||
| 1503 | tuple<Key,Value> v; | ||
| 1504 | array<Option<tuple<Key,Value>>> arr; | ||
| 1505 | Integer pos,lastpos,pos_1; | ||
| 1506 | list<tuple<Key,Value>> res; | ||
| 1507 | case (arr,pos,lastpos) guard pos == lastpos | ||
| 1508 | algorithm | ||
| 1509 | ✗ | SOME(v) := arr[pos + 1]; | |
| 1510 | then | ||
| 1511 | {v}; | ||
| 1512 | case (arr,pos,lastpos) | ||
| 1513 | algorithm | ||
| 1514 | ✗ | pos_1 := pos + 1; | |
| 1515 | ✗ | SOME(v) := arr[pos + 1]; | |
| 1516 | ✗ | res := valueArrayList2(arr, pos_1, lastpos); | |
| 1517 | then | ||
| 1518 | (v :: res); | ||
| 1519 | case (arr,pos,lastpos) | ||
| 1520 | algorithm | ||
| 1521 | ✗ | pos_1 := pos + 1; | |
| 1522 | ✗ | NONE() := arr[pos + 1]; | |
| 1523 | ✗ | res := valueArrayList2(arr, pos_1, lastpos); | |
| 1524 | then | ||
| 1525 | (res); | ||
| 1526 | end matchcontinue; | ||
| 1527 | end valueArrayList2; | ||
| 1528 | |||
| 1529 | protected function valueArrayLength | ||
| 1530 | "author: PA | ||
| 1531 | Returns the number of elements in the ValueArray" | ||
| 1532 | input ValueArray valueArray; | ||
| 1533 | output Integer size; | ||
| 1534 | algorithm | ||
| 1535 | size := match valueArray | ||
| 1536 | case VALUE_ARRAY(numberOfElements = size) then size; | ||
| 1537 | end match; | ||
| 1538 | end valueArrayLength; | ||
| 1539 | |||
| 1540 | protected function valueArrayAdd | ||
| 1541 | "author: PA | ||
| 1542 | Adds an entry last to the ValueArray, increasing | ||
| 1543 | array size if no space left by factor 1.4" | ||
| 1544 | input ValueArray valueArray; | ||
| 1545 | input tuple<Key,Value> entry; | ||
| 1546 | output ValueArray outValueArray; | ||
| 1547 | algorithm | ||
| 1548 | outValueArray := matchcontinue valueArray | ||
| 1549 | local | ||
| 1550 | Integer n_1,n,size,expandsize,expandsize_1; | ||
| 1551 | array<Option<tuple<Key,Value>>> arr_1,arr,arr_2; | ||
| 1552 | Real rsize,rexpandsize; | ||
| 1553 | case VALUE_ARRAY(numberOfElements = n,valueArray = arr) guard n < arrayLength(arr) | ||
| 1554 | algorithm | ||
| 1555 | 44 | n_1 := n + 1; | |
| 1556 | 44 | arr_1 := arrayUpdate(arr, n + 1, SOME(entry)); | |
| 1557 | 44 | then | |
| 1558 | VALUE_ARRAY(n_1,arr_1); | ||
| 1559 | |||
| 1560 | case VALUE_ARRAY(numberOfElements = n,valueArray = arr) guard n < arrayLength(arr) | ||
| 1561 | algorithm | ||
| 1562 | size := arrayLength(arr); | ||
| 1563 | ✗ | rsize := intReal(size); | |
| 1564 | ✗ | rexpandsize := rsize * 0.4; | |
| 1565 | ✗ | expandsize := realInt(rexpandsize); | |
| 1566 | expandsize_1 := intMax(expandsize, 1); | ||
| 1567 | ✗ | arr_1 := Array.expand(expandsize_1, arr,NONE()); | |
| 1568 | ✗ | n_1 := n + 1; | |
| 1569 | ✗ | arr_2 := arrayUpdate(arr_1, n + 1, SOME(entry)); | |
| 1570 | ✗ | then | |
| 1571 | VALUE_ARRAY(n_1,arr_2); | ||
| 1572 | else | ||
| 1573 | algorithm | ||
| 1574 | ✗ | print("-InstHierarchyHashTable.valueArrayAdd failed\n"); | |
| 1575 | ✗ | then | |
| 1576 | fail(); | ||
| 1577 | end matchcontinue; | ||
| 1578 | end valueArrayAdd; | ||
| 1579 | |||
| 1580 | protected function valueArraySetnth | ||
| 1581 | "author: PA | ||
| 1582 | Set the n:th variable in the ValueArray to value." | ||
| 1583 | input ValueArray valueArray; | ||
| 1584 | input Integer pos; | ||
| 1585 | input tuple<Key,Value> entry; | ||
| 1586 | output ValueArray outValueArray; | ||
| 1587 | algorithm | ||
| 1588 | outValueArray := matchcontinue valueArray | ||
| 1589 | local | ||
| 1590 | array<Option<tuple<Key,Value>>> arr; | ||
| 1591 | case VALUE_ARRAY(_,arr) guard pos < arrayLength(arr) | ||
| 1592 | algorithm | ||
| 1593 | 694 | arrayUpdate(arr, pos + 1, SOME(entry)); | |
| 1594 | then | ||
| 1595 | valueArray; | ||
| 1596 | else | ||
| 1597 | algorithm | ||
| 1598 | ✗ | print("-InstHierarchyHashTable.valueArraySetnth failed\n"); | |
| 1599 | ✗ | then | |
| 1600 | fail(); | ||
| 1601 | end matchcontinue; | ||
| 1602 | end valueArraySetnth; | ||
| 1603 | |||
| 1604 | protected function valueArrayClearnth | ||
| 1605 | "author: PA | ||
| 1606 | Clears the n:th variable in the ValueArray (set to NONE())." | ||
| 1607 | input ValueArray valueArray; | ||
| 1608 | input Integer pos; | ||
| 1609 | output ValueArray outValueArray; | ||
| 1610 | algorithm | ||
| 1611 | outValueArray := matchcontinue valueArray | ||
| 1612 | local | ||
| 1613 | array<Option<tuple<Key,Value>>> arr; | ||
| 1614 | case VALUE_ARRAY(_,arr) guard pos < arrayLength(arr) | ||
| 1615 | algorithm | ||
| 1616 | ✗ | arrayUpdate(arr, pos + 1,NONE()); | |
| 1617 | then | ||
| 1618 | valueArray; | ||
| 1619 | else | ||
| 1620 | algorithm | ||
| 1621 | ✗ | print("-InstHierarchyHashTable.valueArrayClearnth failed\n"); | |
| 1622 | ✗ | then | |
| 1623 | fail(); | ||
| 1624 | end matchcontinue; | ||
| 1625 | end valueArrayClearnth; | ||
| 1626 | |||
| 1627 | protected function valueArrayNth | ||
| 1628 | "author: PA | ||
| 1629 | Retrieve the n:th Vale from ValueArray, index from 0..n-1." | ||
| 1630 | input ValueArray valueArray; | ||
| 1631 | input Integer pos; | ||
| 1632 | output Key key; | ||
| 1633 | output Value value; | ||
| 1634 | algorithm | ||
| 1635 | (key, value) := match valueArray | ||
| 1636 | local | ||
| 1637 | Key k; | ||
| 1638 | Value v; | ||
| 1639 | Integer n; | ||
| 1640 | array<Option<tuple<Key,Value>>> arr; | ||
| 1641 | |||
| 1642 | case VALUE_ARRAY(numberOfElements = n,valueArray = arr) | ||
| 1643 | algorithm | ||
| 1644 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2094 times.
|
2094 | true := (pos < n); |
| 1645 |
3/6✗ Branch 1 not taken.
✓ Branch 2 taken 2094 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2094 times.
✓ Branch 5 taken 2094 times.
✗ Branch 6 not taken.
|
2094 | SOME((k,v)) := arr[pos + 1]; |
| 1646 | then | ||
| 1647 | (k, v); | ||
| 1648 | |||
| 1649 | end match; | ||
| 1650 | end valueArrayNth; | ||
| 1651 | |||
| 1652 | annotation(__OpenModelica_Interface="frontend"); | ||
| 1653 | end InnerOuter; | ||
| 1654 |