OMCompiler/Compiler/Template/CodegenUtil.tpl
| 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 | // This file defines util functions for templates for transforming Modelica/MetaModelica code to C like | ||
| 37 | // code. They are used in the code generator phase of the compiler to write | ||
| 38 | // target code. | ||
| 39 | // | ||
| 40 | // There are two root templates intended to be called from the code generator: | ||
| 41 | // translateModel and translateFunctions. These templates do not return any | ||
| 42 | // result but instead write the result to files. All other templates return | ||
| 43 | // text and are used by the root templates (most of them indirectly). | ||
| 44 | // | ||
| 45 | // To future maintainers of this file: | ||
| 46 | // | ||
| 47 | // - A line like this | ||
| 48 | // # var = "" /*BUFD*/ | ||
| 49 | // declares a text buffer that you can later append text to. It can also be | ||
| 50 | // passed to other templates that in turn can append text to it. In the new | ||
| 51 | // version of Susan it should be written like this instead: | ||
| 52 | // let &var = buffer "" | ||
| 53 | // | ||
| 54 | // - A line like this | ||
| 55 | // ..., Text var /*BUFP*/, ... | ||
| 56 | // declares that a template takes a text buffer as input parameter. In the | ||
| 57 | // new version of Susan it should be written like this instead: | ||
| 58 | // ..., Text &var, ... | ||
| 59 | // | ||
| 60 | // - A line like this: | ||
| 61 | // ..., var /*BUFC*/, ... | ||
| 62 | // passes a text buffer to a template. In the new version of Susan it should | ||
| 63 | // be written like this instead: | ||
| 64 | // ..., &var, ... | ||
| 65 | // | ||
| 66 | // - Style guidelines: | ||
| 67 | // | ||
| 68 | // - Try (hard) to limit each row to 80 characters | ||
| 69 | // | ||
| 70 | // - Code for a template should be indented with 2 spaces | ||
| 71 | // | ||
| 72 | // - Exception to this rule is if you have only a single case, then that | ||
| 73 | // single case can be written using no indentation | ||
| 74 | // | ||
| 75 | // This single case can be seen as a clarification of the input to the | ||
| 76 | // template | ||
| 77 | // | ||
| 78 | // - Code after a case should be indented with 2 spaces if not written on the | ||
| 79 | // same line | ||
| 80 | |||
| 81 | package CodegenUtil | ||
| 82 | |||
| 83 | import interface SimCodeTV; | ||
| 84 | import ExpressionDumpTpl.*; | ||
| 85 | |||
| 86 | /* public */ template symbolName(String modelNamePrefix, String symbolName) | ||
| 87 | "Creates a unique name for the function" | ||
| 88 | ::= | ||
| 89 | modelNamePrefix + "_" + symbolName | ||
| 90 | end symbolName; | ||
| 91 | |||
| 92 | 911798 | template replaceDotAndUnderscore(String str) | |
| 93 | "Replace _ with __ and dot in identifiers with _. | ||
| 94 | For quoted identifiers (those starting with a single quote) the dots are | ||
| 95 | part of the name itself (e.g. 'Modelica.Media.X') and must be sanitized by | ||
| 96 | unquoteIdentifier (-> _2E), not treated as path separators (-> _5F_5F). | ||
| 97 | Otherwise the generated name diverges from the record type name built by | ||
| 98 | AbsynUtil.pathStringUnquoteReplaceDot and the C code fails to compile. See #13009." | ||
| 99 | ::= | ||
| 100 | let str_dots = if stringEq(substring(str,1,1), "'") then str else System.stringReplace(str,".", "_") | ||
| 101 | let str_underscores = System.stringReplace(str_dots, "_", "__") | ||
| 102 | System.unquoteIdentifier(str_underscores) | ||
| 103 | end replaceDotAndUnderscore; | ||
| 104 | |||
| 105 | 1404 | template getGeneralTarget(String str) | |
| 106 | ::= | ||
| 107 | match str | ||
| 108 | case "msvc10" | ||
| 109 | case "msvc12" | ||
| 110 | case "msvc13" | ||
| 111 | case "msvc15" | ||
| 112 | case "msvc19" | ||
| 113 | then "msvc" | ||
| 114 | else str | ||
| 115 | end getGeneralTarget; | ||
| 116 | |||
| 117 | 693993 | template underscorePath(Absyn.Path path) | |
| 118 | "Generate paths with components separated by underscores. | ||
| 119 | Replaces also the . in identifiers with _. | ||
| 120 | The dot might happen for world.gravityAccleration" | ||
| 121 | ::= | ||
| 122 | match path | ||
| 123 | case QUALIFIED(__) then | ||
| 124 | '<%replaceDotAndUnderscore(name)%>_<%underscorePath(path)%>' | ||
| 125 | case IDENT(__) then | ||
| 126 | replaceDotAndUnderscore(name) | ||
| 127 | case FULLYQUALIFIED(__) then | ||
| 128 | underscorePath(path) | ||
| 129 | end underscorePath; | ||
| 130 | |||
| 131 | 377012 | template crefStr(ComponentRef cr) | |
| 132 | "Generates the name of a variable for variable name array. Uses underscores for qualified names. | ||
| 133 | a._b not a.b" | ||
| 134 | ::= | ||
| 135 | match cr | ||
| 136 | case CREF_IDENT(__) then '<%System.unquoteIdentifier(ident)%><%subscriptsStr(subscriptLst)%>' | ||
| 137 | // Are these even needed? Function context should only have CREF_IDENT :) | ||
| 138 | case CREF_QUAL(ident = "$DER") then 'der(<%crefStr(componentRef)%>)' | ||
| 139 | case CREF_QUAL(ident = "$CLKPRE") then 'previous(<%crefStr(componentRef)%>)' | ||
| 140 | case CREF_QUAL(__) then '<%System.unquoteIdentifier(ident)%><%subscriptsStr(subscriptLst)%>._<%crefStr(componentRef)%>' | ||
| 141 | else "CREF_NOT_IDENT_OR_QUAL" | ||
| 142 | end crefStr; | ||
| 143 | |||
| 144 | 2152382 | template crefStrNoUnderscore(ComponentRef cr) | |
| 145 | "Generates the name of a variable for variable name array. | ||
| 146 | However does not use underscores on qualified names. | ||
| 147 | a.b not a._b. | ||
| 148 | |||
| 149 | Used for generating variable names that are exported e.g. to xml files" | ||
| 150 | ::= | ||
| 151 | match cr | ||
| 152 | case CREF_IDENT(__) then | ||
| 153 | '<%ident%><%subscriptsStr(subscriptLst)%>' | ||
| 154 | case CREF_QUAL(ident = "$DER") then 'der(<%crefStrNoUnderscore(componentRef)%>)' | ||
| 155 | case CREF_QUAL(ident = "$CLKPRE") then 'previous(<%crefStrNoUnderscore(componentRef)%>)' | ||
| 156 | case CREF_QUAL(__) then '<%ident%><%subscriptsStr(subscriptLst)%>.<%crefStrNoUnderscore(componentRef)%>' | ||
| 157 | else "CREF_NOT_IDENT_OR_QUAL" | ||
| 158 | end crefStrNoUnderscore; | ||
| 159 | |||
| 160 | 627900 | template subscriptsStr(list<DAE.Subscript> subscripts) | |
| 161 | "Generares subscript part of the name." | ||
| 162 | ::= | ||
| 163 | if subscripts then | ||
| 164 | '[<%subscripts |> s => subscriptStr(s) ;separator=","%>]' | ||
| 165 | end subscriptsStr; | ||
| 166 | |||
| 167 | ✗ | template crefStrMatlabSafe(ComponentRef cr) | |
| 168 | "Generates the name of a variable for variable name array. Used for linearization | ||
| 169 | to generate matlab safe variable names." | ||
| 170 | ::= | ||
| 171 | match cr | ||
| 172 | case CREF_IDENT(__) then '<%ident%><%subscriptsStrMatlabSafe(subscriptLst)%>' | ||
| 173 | case CREF_QUAL(ident = "$DER") then 'der_<%crefStrMatlabSafe(componentRef)%>' | ||
| 174 | case CREF_QUAL(ident = "$CLKPRE") then 'pre_<%crefStrMatlabSafe(componentRef)%>' | ||
| 175 | case CREF_QUAL(__) then '<%ident%><%subscriptsStrMatlabSafe(subscriptLst)%>_<%crefStrMatlabSafe(componentRef)%>' | ||
| 176 | else "CREF_NOT_IDENT_OR_QUAL" | ||
| 177 | end crefStrMatlabSafe; | ||
| 178 | |||
| 179 | ✗ | template subscriptsStrMatlabSafe(list<DAE.Subscript> subscripts) | |
| 180 | "Generares subscript part of the name for matlab safe variable names." | ||
| 181 | ::= | ||
| 182 | if subscripts then | ||
| 183 | '(<%subscripts |> s => subscriptStr(s) ;separator=","%>)' | ||
| 184 | end subscriptsStrMatlabSafe; | ||
| 185 | |||
| 186 | 711975 | template subscriptStr(DAE.Subscript subscript) | |
| 187 | "Generates a single subscript. | ||
| 188 | Only works for constant integer and cref indicies." | ||
| 189 | |||
| 190 | ::= | ||
| 191 | match subscript | ||
| 192 | case INDEX(exp=ICONST(integer=i)) then i | ||
| 193 | case INDEX(exp=BCONST(bool=i)) then i | ||
| 194 | case INDEX(exp=ENUM_LITERAL(name=n)) then dotPath(n) | ||
| 195 | case INDEX(exp=CREF()) then printExpStr(exp) | ||
| 196 | case SLICE(exp=ICONST(integer=i)) then i | ||
| 197 | case INDEX(__) | ||
| 198 | case SLICE(__) then 'UNKNOWN_SUBSCRIPT /* <%escapeCComments(printExpStr(exp))%> */' | ||
| 199 | case WHOLEDIM(__) then "WHOLEDIM" | ||
| 200 | case WHOLE_NONEXP(__) then "WHOLE_NONEXP" | ||
| 201 | else "UNKNOWN_SUBSCRIPT" | ||
| 202 | end subscriptStr; | ||
| 203 | |||
| 204 | /*********************** Comments ************************/ | ||
| 205 | |||
| 206 | 1464753 | template escapeCComments(String stringWithCComments) | |
| 207 | "escape the C comments inside a string, replaces them with /* */->(* *)" | ||
| 208 | ::= '<%System.stringReplace(System.stringReplace(stringWithCComments, "/*", "(*"), "*/", "*)")%>' | ||
| 209 | end escapeCComments; | ||
| 210 | |||
| 211 | 23364 | template crefCComment(SimVar v, String vName) | |
| 212 | "write the C comment for a cref, if it is not to be obfuscated" | ||
| 213 | ::= | ||
| 214 | match v | ||
| 215 | case SIMVAR(isProtected = true) then | ||
| 216 | if stringEq(getConfigString(OBFUSCATE), "none") | ||
| 217 | then '<%escapeCComments(vName)%>' | ||
| 218 | else 'OBFUSCATED' | ||
| 219 | case SIMVAR(__) then | ||
| 220 | if not stringEq(getConfigString(OBFUSCATE), "full") | ||
| 221 | then '<%escapeCComments(vName)%>' | ||
| 222 | else 'OBFUSCATED' | ||
| 223 | end crefCComment; | ||
| 224 | |||
| 225 | 800052 | template crefCCommentWithVariability(SimVar v) | |
| 226 | "write the C comment for a cref with variability" | ||
| 227 | ::= | ||
| 228 | match v | ||
| 229 | case SIMVAR(isProtected = true) then | ||
| 230 | if stringEq(getConfigString(OBFUSCATE), "none") | ||
| 231 | then ' /* <%escapeCComments(crefStrNoUnderscore(name))%> <%variabilityString(varKind)%> */' | ||
| 232 | case SIMVAR(__) then | ||
| 233 | if not stringEq(getConfigString(OBFUSCATE), "full") | ||
| 234 | then ' /* <%escapeCComments(crefStrNoUnderscore(name))%> <%variabilityString(varKind)%> */' | ||
| 235 | end crefCCommentWithVariability; | ||
| 236 | |||
| 237 | /*********************************************************/ | ||
| 238 | |||
| 239 | 240 | template escapeSingleQuoteIdent(String ident) | |
| 240 | "Escape single quotes from quoted identifiers." | ||
| 241 | ::= | ||
| 242 | // TODO: How to replace "'" with "\'" but not "\'" with "\\'"? | ||
| 243 | '<%System.stringReplace(System.stringReplace(ident, "\\'", "\\\\'"), "'", "\\'")%>' | ||
| 244 | end escapeSingleQuoteIdent; | ||
| 245 | |||
| 246 | ✗ | template initDefaultValXml(DAE.Type type_) | |
| 247 | ::= | ||
| 248 | match type_ | ||
| 249 | case T_INTEGER(__) then '0' | ||
| 250 | case T_REAL(__) then '0.0' | ||
| 251 | case T_BOOL(__) then 'false' | ||
| 252 | case T_STRING(__) then '' | ||
| 253 | case T_ENUMERATION(__) then '0' | ||
| 254 | else error(sourceInfo(), 'initial value of unknown type: <%unparseType(type_)%>') | ||
| 255 | end initDefaultValXml; | ||
| 256 | |||
| 257 | 9859 | template initValXml(Exp exp, String stringQuotes) | |
| 258 | ::= | ||
| 259 | match exp | ||
| 260 | case ICONST(__) then integer | ||
| 261 | case RCONST(__) then real | ||
| 262 | case SCONST(__) then '<%stringQuotes%><%Util.escapeModelicaStringToXmlString(string)%><%stringQuotes%>' | ||
| 263 | case BCONST(__) then bool | ||
| 264 | case ARRAY(__) then '<%array |> elem => initValXml(elem, stringQuotes) ;separator=" "%>' | ||
| 265 | case REDUCTION(__) then if Expression.isSimpleLiteralValue(expr, true) then '<%initValXml(expr, stringQuotes)%>' else '' | ||
| 266 | case ENUM_LITERAL(__) then '<%index%>' | ||
| 267 | else error(sourceInfo(), 'initial value of unknown type: <%dumpExp(exp,"\"")%>') | ||
| 268 | end initValXml; | ||
| 269 | |||
| 270 | /********************************************************************* | ||
| 271 | ********************************************************************* | ||
| 272 | * Common XML Functions | ||
| 273 | ********************************************************************* | ||
| 274 | *********************************************************************/ | ||
| 275 | |||
| 276 | 7919 | template getVariablity(BackendDAE.VarKind varKind) | |
| 277 | "Returns the variablity Attribute of ScalarVariable." | ||
| 278 | ::= | ||
| 279 | match varKind | ||
| 280 | case DISCRETE(__) then "discrete" | ||
| 281 | case PARAM(__) then "parameter" | ||
| 282 | case CONST(__) then "constant" | ||
| 283 | else "continuous" | ||
| 284 | end getVariablity; | ||
| 285 | |||
| 286 | 336730 | template variabilityString(BackendDAE.VarKind varKind) | |
| 287 | ::= | ||
| 288 | match varKind | ||
| 289 | case VARIABLE() then "variable" | ||
| 290 | case STATE(derName=NONE()) then 'STATE(<%index%>)' | ||
| 291 | case STATE(derName=SOME(dcr)) then 'STATE(<%index%>,<%crefStrNoUnderscore(dcr)%>)' | ||
| 292 | case STATE_DER() then "STATE_DER" | ||
| 293 | case DUMMY_DER() then "DUMMY_DER" | ||
| 294 | case DUMMY_STATE() then "DUMMY_STATE" | ||
| 295 | case CLOCKED_STATE() then "CLOCKED_STATE" | ||
| 296 | case DISCRETE() then "DISCRETE" | ||
| 297 | case PARAM() then "PARAM" | ||
| 298 | case CONST() then "CONST" | ||
| 299 | case EXTOBJ() then 'EXTOBJ: <%dotPath(fullClassName)%>' | ||
| 300 | case JAC_VAR() then "JACOBIAN_VAR" | ||
| 301 | case JAC_TMP_VAR() then "JACOBIAN_TMP_VAR" | ||
| 302 | case SEED_VAR() then "SEED_VAR" | ||
| 303 | case OPT_CONSTR() then "OPT_CONSTR" | ||
| 304 | case OPT_FCONSTR() then "OPT_FCONSTR" | ||
| 305 | case OPT_INPUT_WITH_DER() then "OPT_INPUT_WITH_DER" | ||
| 306 | case OPT_INPUT_DER() then "OPT_INPUT_DER" | ||
| 307 | case OPT_TGRID() then "OPT_TGRID" | ||
| 308 | case OPT_LOOP_INPUT() then "OPT_LOOP_INPUT" | ||
| 309 | case ALG_STATE() then "ALG_STATE" | ||
| 310 | case DAE_RESIDUAL_VAR() then "DAE_RESIDUAL_VAR" | ||
| 311 | case DAE_AUX_VAR() then "DAE_AUX_VAR" | ||
| 312 | case LOOP_ITERATION() then "LOOP_ITERATION" | ||
| 313 | case LOOP_SOLVED() then "LOOP_SOLVED" | ||
| 314 | else "#UNKNOWN_VARKIND" | ||
| 315 | end match | ||
| 316 | end variabilityString; | ||
| 317 | |||
| 318 | ✗ | template getAliasVar(AliasVariable aliasvar) | |
| 319 | "Returns the alias Attribute of ScalarVariable." | ||
| 320 | ::= | ||
| 321 | match aliasvar | ||
| 322 | case NOALIAS(__) then '"noAlias"' | ||
| 323 | case ALIAS(__) then '"alias" aliasVariable="<%crefStrNoUnderscore(varName)%>"' | ||
| 324 | case NEGATEDALIAS(__) then '"negatedAlias" aliasVariable="<%crefStrNoUnderscore(varName)%>"' | ||
| 325 | else '"noAlias"' | ||
| 326 | end getAliasVar; | ||
| 327 | |||
| 328 | |||
| 329 | /********************************************************************* | ||
| 330 | ********************************************************************* | ||
| 331 | * Paths | ||
| 332 | ********************************************************************* | ||
| 333 | *********************************************************************/ | ||
| 334 | |||
| 335 | 55705 | template dotPath(Absyn.Path path) | |
| 336 | "Generates paths with components separated by dots." | ||
| 337 | ::= | ||
| 338 | match path | ||
| 339 | case QUALIFIED(__) then '<%name%>.<%dotPath(path)%>' | ||
| 340 | case IDENT(__) then name | ||
| 341 | case FULLYQUALIFIED(__) then dotPath(path) | ||
| 342 | end dotPath; | ||
| 343 | |||
| 344 | |||
| 345 | /********************************************************************* | ||
| 346 | ********************************************************************* | ||
| 347 | * Error | ||
| 348 | ********************************************************************* | ||
| 349 | *********************************************************************/ | ||
| 350 | |||
| 351 | ✗ | template error(SourceInfo srcInfo, String errMessage) | |
| 352 | "Example source template error reporting template to be used together with the sourceInfo() magic function. | ||
| 353 | Usage: error(sourceInfo(), <<message>>) " | ||
| 354 | ::= | ||
| 355 | let() = Tpl.addSourceTemplateError(errMessage, srcInfo) | ||
| 356 | << | ||
| 357 | |||
| 358 | #error "<% Error.infoStr(srcInfo) %> <% errMessage %>"<%\n%> | ||
| 359 | >> | ||
| 360 | end error; | ||
| 361 | |||
| 362 | //for completeness; although the error() template above is preferable | ||
| 363 | ✗ | template errorMsg(String errMessage) | |
| 364 | "Example template error reporting template | ||
| 365 | that is reporting only the error message without the usage of source infotmation." | ||
| 366 | ::= | ||
| 367 | let() = Tpl.addTemplateError(errMessage) | ||
| 368 | << | ||
| 369 | |||
| 370 | #error "<% errMessage %>"<%\n%> | ||
| 371 | >> | ||
| 372 | end errorMsg; | ||
| 373 | |||
| 374 | /* public */ template extFunctionName(String name, String language) "used in Compiler/Template/CodegenFMU.tpl" | ||
| 375 | ::= | ||
| 376 | match language | ||
| 377 | case "BUILTIN" | ||
| 378 | case "C" then '<%name%>' | ||
| 379 | case "FORTRAN 77" then '<%name%>_' | ||
| 380 | else error(sourceInfo(), 'Unsupported external language: <%language%>') | ||
| 381 | end extFunctionName; | ||
| 382 | |||
| 383 | annotation(__OpenModelica_Interface="codegen_util"); | ||
| 384 | end CodegenUtil; | ||
| 385 | |||
| 386 | // vim: filetype=susan sw=2 sts=2 | ||
| 387 |