Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 57.8% 625 / 0 / 1081
Functions: -% 0 / 1 / 1
Branches: 54.5% 339 / 0 / 622

OMCompiler/Compiler/Parsers/SimpleModelicaParser.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 SimpleModelicaParser
37
38 import LexerModelicaDiff.{Token,TokenId,tokenContent,printToken,modelicaDiffTokenEq};
39 import DoubleEnded;
40
41 protected
42
43 import AvlSetString;
44 import DiffAlgorithm;
45 import DiffAlgorithm.{diff,Diff};
46 import Error;
47 import Print;
48 import StackOverflow;
49 import System;
50 import List;
51 import StringUtil;
52 import MetaModelica.Dangerous.listReverseInPlace;
53
54 constant Token newlineToken = Token.TOKEN("", TokenId.NEWLINE, "\n", 1, 1, 1, 1, 1, 1);
55
56 public
57
58 uniontype ParseTree
59 record EMPTY
60 end EMPTY;
61 record NODE
62 ParseTree label;
63 list<ParseTree> nodes;
64 end NODE;
65 record LEAF
66 Token token;
67 end LEAF;
68 end ParseTree;
69
70 function parseTreeStr
71 input list<ParseTree> trees;
72 output String str;
73 protected
74 Integer i;
75 algorithm
76 183 i := Print.saveAndClearBuf();
77 try
78
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 183 times.
366 for tree in trees loop
79 183 parseTreeStrWork(tree);
80 end for;
81 183 str := Print.getString();
82 183 Print.restoreBuf(i);
83 else
84 ✗ Print.restoreBuf(i);
85 ✗ fail();
86 end try;
87 end parseTreeStr;
88
89 function treeDiff
90 input list<ParseTree> t1, t2;
91 input Integer nTokens "The number of tokens in the larger tree; used to allocate arrays. Should be enough with the smaller tree, but there are no additional bounds checks this way.";
92 output list<tuple<Diff,list<ParseTree>>> res;
93 protected
94 ParseTree within1, within2;
95 list<ParseTree> t1_updated, t2_updated;
96 algorithm
97 46 within1 := findWithin(t1);
98 46 within2 := findWithin(t2);
99 // If the new file lacks a within that was in the first file, pretend it is there
100 // The other option is to preserve within in OMEdit...
101 (t1_updated, t2_updated) := match (within1,within2)
102 case (EMPTY(), EMPTY()) then (t1,t2);
103 case (_, EMPTY()) then (t1, within1::LEAF(newlineToken)::t2);
104 case (EMPTY(), _) then (within2::LEAF(newlineToken)::LEAF(newlineToken)::t1, t2);
105 else (t1,t2);
106 end match;
107 // t2_updated := moveComments(t1_updated, t2_updated);
108 46 res := treeDiffWork1(t1_updated, t2_updated, nTokens);
109 // res := moveCommentsAfterDiff(res);
110 end treeDiff;
111
112 partial function CmpParseTreeFunc
113 input ParseTree t1, t2;
114 output Boolean b;
115 end CmpParseTreeFunc;
116
117 function parseTreeNodeStr
118 input ParseTree tree;
119 output String str;
120 protected
121 Integer i;
122 algorithm
123 944 i := Print.saveAndClearBuf();
124 try
125 944 parseTreeStrWork(tree);
126 944 str := Print.getString();
127 944 Print.restoreBuf(i);
128 else
129 ✗ Print.restoreBuf(i);
130 ✗ fail();
131 end try;
132 end parseTreeNodeStr;
133
134 partial function partialParser
135 input list<Token> inTokens;
136 input list<ParseTree> inTree;
137 output list<Token> tokens = inTokens;
138 output list<ParseTree> outTree;
139 protected
140 list<ParseTree> tree = {};
141 end partialParser;
142
143 function stored_definition
144 extends partialParser;
145 protected
146 Boolean b;
147 algorithm
148 92 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.WITHIN);
149
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 84 times.
92 if b then
150 8 (tokens, tree, b) := LA1(tokens, tree, First.name);
151
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7 times.
8 if b then
152 1 (tokens, tree) := name(tokens, tree);
153 end if;
154 8 (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
155 8 outTree := makeNode(listReverse(tree), label=LEAF(makeToken(TokenId.IDENT, "$within")))::{};
156 8 tree := {};
157 else
158 outTree := {};
159 end if;
160
161 92 (tokens, tree, b) := LA1(tokens, tree, First.class_definition);
162
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 92 times.
184 while b loop
163 92 (tokens, tree, ) := scanOpt(tokens, tree, TokenId.FINAL);
164 92 (tokens, tree) := class_definition(tokens, tree);
165 92 (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
166 92 (tokens, tree, b) := LA1(tokens, tree, First.class_definition);
167 92 outTree := makeNode(listReverse(tree))::outTree;
168 92 tree := {};
169 end while;
170 // Eat trailing whitespace so nothing is stripped
171 92 (tokens, tree) := eatWhitespace(tokens, tree);
172 // Do an EOF check
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if not listEmpty(tokens) then
174 ✗ error(tokens, tree, {});
175 end if;
176 92 outTree := makeNode(listReverse(listAppend(tree, listAppend(outTree, inTree))), label=LEAF(makeToken(TokenId.IDENT, "$program")))::{};
177 end stored_definition;
178
179 protected
180
181 function class_definition
182 extends partialParser;
183 output ParseTree nodeName;
184 algorithm
185 tree := {};
186 98 (tokens, tree) := scanOpt(tokens, tree, TokenId.ENCAPSULATED);
187 98 (tokens, tree) := class_prefixes(tokens, tree);
188 98 (tokens, tree, nodeName) := class_specifier(tokens, tree);
189 98 outTree := makeNode(listReverse(tree), label=nodeName)::inTree;
190 end class_definition;
191
192 function class_prefixes
193 extends partialParser;
194 protected
195 TokenId id;
196 Boolean b;
197 algorithm
198 98 (tokens, tree) := scanOpt(tokens, tree, TokenId.PARTIAL);
199 98 (tokens, tree, id) := peek(tokens, tree);
200 () := match id
201 case TokenId.OPERATOR
202 algorithm
203 ✗ (tokens, tree) := consume(tokens, tree);
204 ✗ (tokens, tree) := LA1(tokens, tree, {TokenId.RECORD, TokenId.FUNCTION}, consume=true);
205 then ();
206 case TokenId.EXPANDABLE
207 algorithm
208 ✗ (tokens, tree) := consume(tokens, tree);
209 ✗ (tokens, tree) := scan(tokens, tree, TokenId.CONNECTOR);
210 then ();
211 case id guard listMember(id, {TokenId.PURE, TokenId.IMPURE})
212 algorithm
213 ✗ (tokens, tree) := consume(tokens, tree);
214 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.OPERATOR);
215 ✗ (tokens, tree) := scanOneOf(tokens, tree, if b then {TokenId.FUNCTION} else {TokenId.FUNCTION, TokenId.RECORD});
216 then ();
217 else
218 algorithm
219 98 (tokens, tree) := scanOneOf(tokens, tree, {TokenId.CLASS, TokenId.MODEL, TokenId.RECORD, TokenId.BLOCK, TokenId.CONNECTOR, TokenId.TYPE, TokenId.PACKAGE, TokenId.FUNCTION, TokenId.OPERATOR});
220 then ();
221 end match;
222 98 outTree := makeNodePrependTree(listReverse(tree), inTree);
223 end class_prefixes;
224
225 function class_specifier
226 extends partialParser;
227 output ParseTree nodeName;
228 protected
229 Boolean b;
230 algorithm
231 98 tree := inTree;
232 98 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.IDENT);
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 nodeName::_ := tree;
234 98 nodeName := parseTreeFilterWhitespace(nodeName);
235
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 2 times.
98 if b then
236 96 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.EQUALS);
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if b then
238 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.DER);
239 ✗ if b then
240 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
241 ✗ (tokens, tree) := name(tokens, tree);
242 ✗ (tokens, tree) := scan(tokens, tree, TokenId.COMMA);
243 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
244 ✗ while true loop
245 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
246 ✗ if not b then
247 break;
248 end if;
249 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
250 end while;
251 ✗ (tokens, tree) := scan(tokens, tree, TokenId.RPAR);
252 ✗ (tokens, tree) := comment(tokens, tree);
253 else
254 ✗ (tokens, tree) := short_class_specifier1(tokens, tree);
255 end if;
256 else
257 96 (tokens, tree) := string_comment(tokens, tree);
258 96 (tokens, tree) := composition(tokens, tree);
259 96 (tokens, tree) := scan(tokens, tree, TokenId.END);
260 96 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
261 end if;
262 else
263 2 (tokens, tree) := scan(tokens, tree, TokenId.EXTENDS);
264 2 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
265 2 (tokens, tree, b) := LA1(tokens, tree, First.class_modification);
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if b then
267 ✗ (tokens, tree) := class_modification(tokens, tree);
268 end if;
269 2 (tokens, tree) := string_comment(tokens, tree);
270 2 (tokens, tree) := composition(tokens, tree);
271 2 (tokens, tree) := scan(tokens, tree, TokenId.END);
272 2 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
273 end if;
274
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 outTree := tree;
275 end class_specifier;
276
277 function short_class_specifier1
278 extends partialParser;
279 protected
280 Boolean b;
281 algorithm
282 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ENUMERATION);
283 ✗ if b then
284 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
285 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COLON);
286 ✗ if not b then
287 while true loop
288 ✗ (tokens, tree) := enumeration_literal(tokens, tree);
289 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
290 ✗ if not b then
291 break;
292 end if;
293 end while;
294 end if;
295 ✗ (tokens, tree) := scan(tokens, tree, TokenId.RPAR);
296 else
297 ✗ (tokens, tree) := base_prefix(tokens, tree);
298 ✗ (tokens, tree) := name(tokens, tree);
299 ✗ (tokens, tree, b) := LA1(tokens, tree, {TokenId.LBRACK});
300 ✗ if b then
301 ✗ (tokens, tree) := array_subscripts(tokens, tree);
302 end if;
303 ✗ (tokens, tree, b) := LA1(tokens, tree, First.class_modification);
304 ✗ if b then
305 ✗ (tokens, tree) := class_modification(tokens, tree);
306 end if;
307 end if;
308 ✗ (tokens, tree) := comment(tokens, tree);
309 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
310 end short_class_specifier1;
311
312 function enumeration_literal
313 extends partialParser;
314 protected
315 algorithm
316 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
317 ✗ (tokens, tree) := comment(tokens, tree);
318 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
319 end enumeration_literal;
320
321 function composition
322 extends partialParser;
323 protected
324 TokenId id;
325 Boolean b;
326 algorithm
327 98 (tokens, tree) := element_list(tokens, tree);
328 while true loop
329 132 (tokens, tree, b) := LA1(tokens, tree, {TokenId.PROTECTED, TokenId.PUBLIC, TokenId.INITIAL, TokenId.EQUATION, TokenId.ALGORITHM});
330
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 98 times.
132 if not b then
331 break;
332 end if;
333 34 (tokens, tree, b) := LA1(tokens, tree, {TokenId.PROTECTED, TokenId.PUBLIC}, consume=true);
334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if b then
335 ✗ (tokens, tree) := element_list(tokens, tree);
336 else
337 34 (tokens, tree) := scanOpt(tokens, tree, TokenId.INITIAL);
338 34 (tokens, tree, b) := LA1(tokens, tree, {TokenId.ALGORITHM});
339
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 6 times.
34 if b then
340 28 (tokens, tree) := algorithm_section(tokens, tree);
341 else
342 6 (tokens, tree) := equation_section(tokens, tree);
343 end if;
344 end if;
345 end while;
346 98 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.EXTERNAL);
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 if b then
348 ✗ (tokens, tree) := scanOpt(tokens, tree, TokenId.STRING); // language
349 ✗ (tokens, tree, id) := peek(tokens, tree);
350 ✗ if not (id==TokenId.ANNOTATION or id==TokenId.SEMICOLON) then
351 ✗ (tokens, tree) := external_function_call(tokens, tree);
352 end if;
353 ✗ (tokens, tree, b) := LA1(tokens, tree, First._annotation);
354 ✗ if b then
355 ✗ (tokens, tree) := _annotation(tokens, tree);
356 end if;
357 ✗ (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
358 end if;
359
360 98 b := true;
361
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 98 times.
196 while b loop
362 // OMEdit can sometimes insert multiple annotation-sections...
363 98 (tokens, tree, b) := LA1(tokens, tree, First._annotation);
364
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 if b then
365 ✗ (tokens, tree) := _annotation(tokens, tree);
366 ✗ (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
367 end if;
368 end while;
369
370 98 outTree := makeNodePrependTree(listReverse(tree), inTree);
371 end composition;
372
373 function external_function_call
374 extends partialParser;
375 protected
376 Boolean b;
377 algorithm
378 ✗ (tokens, tree, b) := LAk(tokens, tree, {{TokenId.IDENT},{TokenId.LPAR}});
379 ✗ if not b then
380 ✗ (tokens, tree) := component_reference(tokens, tree);
381 ✗ (tokens, tree) := scan(tokens, tree, TokenId.EQUALS);
382 end if;
383 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
384 ✗ (tokens, tree) := output_expression_list(tokens, tree);
385 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
386 end external_function_call;
387
388 function algorithm_section
389 extends partialParser;
390 protected
391 algorithm
392 28 (tokens, tree) := scanOpt(tokens, tree, TokenId.INITIAL);
393 28 (tokens, tree) := scan(tokens, tree, TokenId.ALGORITHM);
394 28 (tokens, tree) := statement_list(tokens, tree);
395 28 outTree := makeNodePrependTree(listReverse(tree), inTree, label=LEAF(makeToken(TokenId.IDENT, "$algorithm_section")));
396 end algorithm_section;
397
398 function statement
399 extends partialParser;
400 output String label="$statement";
401 protected
402 TokenId id;
403 Boolean b;
404 algorithm
405 76 (tokens, tree, id) := peek(tokens, tree);
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if id==TokenId.BREAK or id==TokenId.RETURN then
407 ✗ (tokens, tree) := consume(tokens, tree);
408 ✗ label := "$"+String(id);
409 elseif listMember(id, First.component_reference) then
410 52 (tokens, tree) := component_reference(tokens, tree);
411 52 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ASSIGN);
412
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 2 times.
52 if b then
413 50 (tokens, tree) := expression(tokens, tree);
414 label := "$assign";
415 else
416 2 (tokens, tree) := function_call_args(tokens, tree);
417 label := "$statement_call";
418 end if;
419 elseif id==TokenId.IF then
420 24 (tokens, tree) := consume(tokens, tree);
421 24 (tokens, tree) := expression(tokens, tree);
422 24 (tokens, tree) := scan(tokens, tree, TokenId.THEN);
423 24 (tokens, tree) := statement_list(tokens, tree);
424 ✗ while true loop
425 24 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSEIF);
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if not b then
427 break;
428 end if;
429 ✗ (tokens, tree) := expression(tokens, tree);
430 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
431 ✗ (tokens, tree) := statement_list(tokens, tree);
432 end while;
433 24 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSE);
434
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if b then
435 24 (tokens, tree) := statement_list(tokens, tree);
436 end if;
437 24 (tokens, tree) := scan(tokens, tree, TokenId.END);
438 24 (tokens, tree) := scan(tokens, tree, TokenId.IF);
439 label := "$if";
440 elseif id==TokenId.WHEN then
441 ✗ (tokens, tree) := consume(tokens, tree);
442 ✗ (tokens, tree) := expression(tokens, tree);
443 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
444 ✗ (tokens, tree) := statement_list(tokens, tree);
445 ✗ while true loop
446 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSEWHEN);
447 ✗ if not b then
448 break;
449 end if;
450 ✗ (tokens, tree) := expression(tokens, tree);
451 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
452 ✗ (tokens, tree) := statement_list(tokens, tree);
453 end while;
454 ✗ (tokens, tree) := scan(tokens, tree, TokenId.END);
455 ✗ (tokens, tree) := scan(tokens, tree, TokenId.WHEN);
456 label := "$when";
457 elseif id==TokenId.FOR then
458 ✗ (tokens, tree) := consume(tokens, tree);
459 ✗ (tokens, tree) := for_indices(tokens, tree);
460 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LOOP);
461 ✗ (tokens, tree) := statement_list(tokens, tree);
462 ✗ (tokens, tree) := scan(tokens, tree, TokenId.END);
463 ✗ (tokens, tree) := scan(tokens, tree, TokenId.FOR);
464 label := "$for";
465 elseif id==TokenId.WHILE then
466 ✗ (tokens, tree) := consume(tokens, tree);
467 ✗ (tokens, tree) := expression(tokens, tree);
468 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LOOP);
469 ✗ (tokens, tree) := statement_list(tokens, tree);
470 ✗ (tokens, tree) := scan(tokens, tree, TokenId.END);
471 ✗ (tokens, tree) := scan(tokens, tree, TokenId.WHILE);
472 label := "$while";
473 else
474 ✗ (tokens, tree) := expression(tokens, tree);
475 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ASSIGN);
476 ✗ if b then
477 ✗ (tokens, tree) := expression(tokens, tree);
478 end if;
479 label := "$assign_expression";
480 end if;
481 76 (tokens, tree) := comment(tokens, tree);
482 76 outTree := makeNodePrependTree(listReverse(tree), inTree);
483 end statement;
484
485 function statement_list
486 extends partialParser;
487 protected
488 Boolean b;
489 String label;
490 algorithm
491 outTree := {};
492 76 while true loop
493 152 (tokens, tree, b) := LA1(tokens, tree, Follow.statement_equation);
494
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 76 times.
152 if b then
495 break;
496 end if;
497 76 (tokens, tree, label) := statement(tokens, tree);
498 76 (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
499
500 76 outTree := makeNode(listReverse(tree), label=LEAF(makeToken(TokenId.IDENT, label)))::outTree;
501 76 tree := {};
502 end while;
503 76 outTree := listAppend(tree, listAppend(outTree, inTree));
504 end statement_list;
505
506 function equation_section
507 extends partialParser;
508 protected
509 algorithm
510 6 (tokens, tree) := scanOpt(tokens, tree, TokenId.INITIAL);
511 6 (tokens, tree) := scan(tokens, tree, TokenId.EQUATION);
512 6 (tokens, tree) := equation_list(tokens, tree);
513 6 outTree := makeNodePrependTree(listReverse(tree), inTree, label=LEAF(makeToken(TokenId.IDENT, "$equation_section")));
514 end equation_section;
515
516 function _equation
517 extends partialParser;
518 output String label = "$equation";
519 protected
520 TokenId id;
521 Boolean b;
522 algorithm
523 6 (tokens, tree, id) := peek(tokens, tree);
524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if id==TokenId.IF then
525 ✗ (tokens, tree) := consume(tokens, tree);
526 ✗ (tokens, tree) := expression(tokens, tree);
527 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
528 ✗ (tokens, tree) := equation_list(tokens, tree);
529 ✗ while true loop
530 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSEIF);
531 ✗ if not b then
532 break;
533 end if;
534 ✗ (tokens, tree) := expression(tokens, tree);
535 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
536 ✗ (tokens, tree) := equation_list(tokens, tree);
537 end while;
538 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSE);
539 ✗ if b then
540 ✗ (tokens, tree) := equation_list(tokens, tree);
541 end if;
542 ✗ (tokens, tree) := scan(tokens, tree, TokenId.END);
543 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IF);
544 label := "$if_equation";
545 elseif id==TokenId.WHEN then
546 ✗ (tokens, tree) := consume(tokens, tree);
547 ✗ (tokens, tree) := expression(tokens, tree);
548 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
549 ✗ (tokens, tree) := equation_list(tokens, tree);
550 ✗ while true loop
551 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSEWHEN);
552 ✗ if not b then
553 break;
554 end if;
555 ✗ (tokens, tree) := expression(tokens, tree);
556 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
557 ✗ (tokens, tree) := equation_list(tokens, tree);
558 end while;
559 ✗ (tokens, tree) := scan(tokens, tree, TokenId.END);
560 ✗ (tokens, tree) := scan(tokens, tree, TokenId.WHEN);
561 label := "$when_equation";
562 elseif id==TokenId.FOR then
563 ✗ (tokens, tree) := consume(tokens, tree);
564 ✗ (tokens, tree) := for_indices(tokens, tree);
565 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LOOP);
566 ✗ (tokens, tree) := equation_list(tokens, tree);
567 ✗ (tokens, tree) := scan(tokens, tree, TokenId.END);
568 ✗ (tokens, tree) := scan(tokens, tree, TokenId.FOR);
569 label := "$for_equation";
570 elseif id==TokenId.CONNECT then
571 2 (tokens, tree) := consume(tokens, tree);
572 2 (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
573 2 (tokens, tree) := component_reference(tokens, tree);
574 2 (tokens, tree) := scan(tokens, tree, TokenId.COMMA);
575 2 (tokens, tree) := component_reference(tokens, tree);
576 2 (tokens, tree) := scan(tokens, tree, TokenId.RPAR);
577 label := "$connect_equation";
578 else
579 4 (tokens, tree) := expression(tokens, tree);
580 4 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.EQUALS);
581
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if b then
582 4 (tokens, tree) := expression(tokens, tree);
583 label := "$equality_equation";
584 else
585 label := "$singleton_equation";
586 end if;
587 end if;
588 6 (tokens, tree) := comment(tokens, tree);
589 6 outTree := makeNodePrependTree(listReverse(tree), inTree);
590 end _equation;
591
592 function equation_list
593 extends partialParser;
594 protected
595 Boolean b;
596 String label;
597 algorithm
598 outTree := {};
599 6 while true loop
600 12 (tokens, tree, b) := LA1(tokens, tree, Follow.statement_equation);
601
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if b then
602 break;
603 end if;
604 6 (tokens, tree, label) := _equation(tokens, tree);
605 6 (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
606
607 6 outTree := makeNode(listReverse(tree), label=LEAF(makeToken(TokenId.IDENT, label)))::outTree;
608 6 tree := {};
609 end while;
610 6 outTree := listAppend(tree, listAppend(outTree, inTree));
611 end equation_list;
612
613 function element_list
614 extends partialParser;
615 protected
616 Boolean b, isAnnotation;
617 ParseTree nodeName;
618 algorithm
619 outTree := {};
620 while true loop
621 179 (tokens, tree, b) := LA1(tokens, tree, First.element);
622
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 98 times.
179 if not b then
623 break;
624 end if;
625 81 (tokens, tree, nodeName, isAnnotation) := element(tokens, tree);
626 81 (tokens, tree) := scan(tokens, tree, TokenId.SEMICOLON);
627
628
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 79 times.
81 if not isAnnotation then
629 79 outTree := makeNode(listReverse(tree), label=nodeName)::outTree;
630 79 tree := {};
631 end if;
632 end while;
633 98 outTree := listAppend(tree, listAppend(outTree, inTree));
634 end element_list;
635
636 function element
637 extends partialParser;
638 output ParseTree nodeName = LEAF(makeToken(TokenId.IDENT, "$element"));
639 output Boolean isAnnotation = false;
640 protected
641 TokenId id;
642 Boolean b,b1;
643 algorithm
644 81 (tokens, tree, id) := peek(tokens, tree);
645 nodeName := match id
646 case TokenId.IMPORT
647 algorithm
648 ✗ (tokens, tree) := import_clause(tokens, tree);
649 ✗ then LEAF(makeToken(TokenId.IDENT, "$import"));
650 case TokenId.EXTENDS
651 algorithm
652 ✗ (tokens, tree) := extends_clause(tokens, tree);
653 ✗ then LEAF(makeToken(TokenId.IDENT, "$extends"));
654 case TokenId.ANNOTATION
655 algorithm
656 2 (tokens, tree) := _annotation(tokens, tree);
657 isAnnotation := true;
658 2 then LEAF(makeToken(TokenId.IDENT, "$annotation"));
659 else
660 algorithm
661 79 (tokens, tree) := scanOpt(tokens, tree, TokenId.REDECLARE);
662 79 (tokens, tree) := scanOpt(tokens, tree, TokenId.FINAL);
663 79 (tokens, tree) := scanOpt(tokens, tree, TokenId.INNER);
664 79 (tokens, tree) := scanOpt(tokens, tree, TokenId.OUTER);
665 79 (tokens, tree, b1) := scanOpt(tokens, tree, TokenId.REPLACEABLE);
666 79 (tokens, tree, b) := LA1(tokens, tree, First.class_definition);
667
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 73 times.
79 if b then
668 6 (tokens, tree, nodeName) := class_definition(tokens, tree);
669 else
670 73 (tokens, tree, nodeName) := component_clause(tokens, tree);
671 end if;
672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if b1 then
673 ✗ (tokens, tree, b) := LA1(tokens, tree, {TokenId.CONSTRAINEDBY});
674 ✗ if b then
675 ✗ (tokens, tree) := constraining_clause(tokens, tree);
676 ✗ (tokens, tree) := comment(tokens, tree);
677 end if;
678 end if;
679 79 then nodeName;
680 end match;
681 81 outTree := makeNodePrependTree(listReverse(tree), inTree);
682 end element;
683
684 function constraining_clause
685 extends partialParser;
686 protected
687 Boolean b;
688 algorithm
689 ✗ (tokens, tree) := scan(tokens, tree, TokenId.CONSTRAINEDBY);
690 ✗ (tokens, tree) := name(tokens, tree);
691 ✗ (tokens, tree, b) := LA1(tokens, tree, First.class_modification);
692 ✗ if b then
693 ✗ (tokens, tree) := class_modification(tokens, tree);
694 end if;
695 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
696 end constraining_clause;
697
698 function component_clause
699 extends partialParser;
700 output ParseTree nodeName;
701 protected
702 Boolean b;
703 list<ParseTree> nodeNames;
704 algorithm
705 73 (tokens, tree) := type_prefix(tokens, tree);
706 73 (tokens, tree) := type_specifier(tokens, tree);
707 73 (tokens, tree, b) := LA1(tokens, tree, {TokenId.LBRACK});
708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
73 if b then
709 ✗ (tokens, tree) := array_subscripts(tokens, tree);
710 end if;
711 73 tree := makeNode(listReverse(tree), label=LEAF(makeToken(TokenId.IDENT, "$type_specifier")))::{};
712 73 (tokens, tree, nodeNames) := component_list(tokens, tree);
713
8/8
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 73 times.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 73 times.
✓ Branch 7 taken 73 times.
✓ Branch 8 taken 73 times.
✓ Branch 9 taken 73 times.
✓ Branch 10 taken 73 times.
365 nodeName := LEAF(makeToken(TokenId.IDENT, "$component:"+stringDelimitList(list(parseTreeStr(name::{}) for name in nodeNames),",")));
714 73 outTree := makeNodePrependTree(listReverse(tree), inTree, label=nodeName);
715 end component_clause;
716
717 function import_clause
718 extends partialParser;
719 protected
720 Boolean b;
721 algorithm
722 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IMPORT);
723 ✗ (tokens, tree, b) := LAk(tokens, tree, {{TokenId.IDENT}, {TokenId.EQUALS}});
724 ✗ if b then
725 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
726 ✗ (tokens, tree) := scan(tokens, tree, TokenId.EQUALS);
727 ✗ (tokens, tree) := name(tokens, tree);
728 else
729 ✗ (tokens, tree) := name(tokens, tree);
730 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.STAR_EW);
731 ✗ if not b then
732 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.DOT);
733 ✗ if b then
734 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LBRACE);
735 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
736 ✗ while true loop
737 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
738 ✗ if not b then
739 break;
740 end if;
741 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.IDENT);
742 end while;
743 ✗ (tokens, tree) := scan(tokens, tree, TokenId.RBRACE);
744 end if;
745 end if;
746 end if;
747 ✗ (tokens, tree) := comment(tokens, tree);
748 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
749 end import_clause;
750
751 function type_specifier = name;
752 function base_prefix = type_prefix;
753
754 function type_prefix
755 extends partialParser;
756 protected
757 algorithm
758 73 (tokens, tree) := LA1(tokens, tree, {TokenId.FLOW, TokenId.STREAM}, consume=true);
759 73 (tokens, tree) := LA1(tokens, tree, {TokenId.DISCRETE, TokenId.PARAMETER, TokenId.CONSTANT}, consume=true);
760 73 (tokens, tree) := LA1(tokens, tree, {TokenId.INPUT, TokenId.OUTPUT}, consume=true);
761 73 outTree := makeNodePrependTree(listReverse(tree), inTree);
762 end type_prefix;
763
764 function array_subscripts
765 extends partialParser;
766 protected
767 Boolean b;
768 algorithm
769 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LBRACK);
770 ✗ (tokens, tree) := subscript(tokens, tree);
771 ✗ while true loop
772 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
773 ✗ if not b then
774 break;
775 end if;
776 ✗ (tokens, tree) := subscript(tokens, tree);
777 end while;
778 ✗ (tokens, tree) := scan(tokens, tree, TokenId.RBRACK);
779 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
780 end array_subscripts;
781
782 function subscript
783 extends partialParser;
784 protected
785 Boolean b;
786 algorithm
787 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COLON);
788 ✗ if not b then
789 ✗ (tokens, tree) := expression(tokens, tree);
790 end if;
791 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
792 end subscript;
793
794 function component_list
795 extends partialParser;
796 output list<ParseTree> nodeNames={};
797 protected
798 Boolean b;
799 ParseTree nodeName;
800 algorithm
801 73 (tokens, tree, nodeName) := component_declaration(tokens, tree);
802 73 nodeNames := nodeName::nodeNames;
803 ✗ while true loop
804 73 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
73 if not b then
806 break;
807 end if;
808 ✗ (tokens, tree, nodeName) := component_declaration(tokens, tree);
809 ✗ nodeNames := nodeName::nodeNames;
810 end while;
811 73 outTree := makeNodePrependTree(listReverse(tree), inTree);
812 end component_list;
813
814 function component_declaration
815 extends partialParser;
816 output ParseTree nodeName;
817 protected
818 Boolean b;
819 algorithm
820 73 (tokens, tree, nodeName) := declaration(tokens, tree);
821 73 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.IF);
822
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 71 times.
73 if b then
823 2 (tokens, tree) := expression(tokens, tree);
824 end if;
825 // print("component_declaration: comment1 "+topTokenStr(tokens)+"\n");
826 73 (tokens, tree) := comment(tokens, tree);
827 // print("component_declaration: comment2 "+topTokenStr(tokens)+"\n");
828 73 outTree := makeNodePrependTree(listReverse(tree), inTree, label=nodeName);
829 end component_declaration;
830
831 function declaration
832 extends partialParser;
833 output ParseTree nodeName;
834 protected
835 Boolean b;
836 algorithm
837 73 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
838
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
73 nodeName::_ := tree;
839 73 nodeName := parseTreeFilterWhitespace(nodeName);
840 73 (tokens, tree, b) := LA1(tokens, tree, {TokenId.LBRACK});
841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
73 if b then
842 ✗ (tokens, tree) := array_subscripts(tokens, tree);
843 end if;
844 73 (tokens, tree, b) := LA1(tokens, tree, First.modification);
845 // print("declaration has modification?: "+String(b)+"\n");
846
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 63 times.
73 if b then
847 // print("before mod: "+topTokenStr(tokens)+"\n");
848 10 (tokens, tree) := modification(tokens, tree);
849 // print("after mod: "+topTokenStr(tokens)+"\n");
850 end if;
851 73 outTree := makeNodePrependTree(listReverse(tree), inTree);
852 end declaration;
853
854 function component_clause1
855 extends partialParser;
856 output ParseTree nodeName;
857 protected
858 algorithm
859 ✗ (tokens, tree) := type_prefix(tokens, tree);
860 ✗ (tokens, tree) := type_specifier(tokens, tree);
861 ✗ (tokens, tree, nodeName) := component_declaration1(tokens, tree);
862 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
863 end component_clause1;
864
865 function component_declaration1
866 extends partialParser;
867 output ParseTree nodeName;
868 protected
869 algorithm
870 ✗ (tokens, tree, nodeName) := declaration(tokens, tree);
871 ✗ (tokens, tree) := comment(tokens, tree);
872 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
873 end component_declaration1;
874
875 function extends_clause
876 extends partialParser;
877 protected
878 Boolean b;
879 algorithm
880 ✗ (tokens, tree) := scan(tokens, tree, TokenId.EXTENDS);
881 ✗ (tokens, tree) := name(tokens, tree);
882 ✗ (tokens, tree, b) := LA1(tokens, tree, First.class_modification);
883 ✗ if b then
884 ✗ (tokens, tree) := class_modification(tokens, tree);
885 end if;
886 ✗ (tokens, tree, b) := LA1(tokens, tree, First._annotation);
887 ✗ if b then
888 ✗ (tokens, tree) := _annotation(tokens, tree);
889 end if;
890 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
891 end extends_clause;
892
893 function class_modification
894 extends partialParser;
895 protected
896 Boolean b;
897 algorithm
898 27 (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
899 27 (tokens, tree, b) := LA1(tokens, tree, First.argument);
900
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if b then
901 27 (tokens, tree) := argument_list(tokens, tree);
902 end if;
903 27 (tokens, tree) := scan(tokens, tree, TokenId.RPAR);
904 27 outTree := makeNodePrependTree(listReverse(tree), inTree);
905 end class_modification;
906
907 function argument_list
908 extends partialParser;
909 protected
910 Boolean b;
911 ParseTree nodeName;
912 algorithm
913 27 (tokens, tree, nodeName) := argument(tokens, tree);
914 27 b := true;
915
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 27 times.
74 while b loop
916 47 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
917
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 20 times.
47 if b then
918 20 (tokens, tree, nodeName) := argument(tokens, tree);
919 end if;
920 end while;
921 27 outTree := makeNodePrependTree(listReverse(tree), inTree);
922 end argument_list;
923
924 function argument
925 extends partialParser;
926 output ParseTree nodeName;
927 protected
928 Boolean b;
929 ParseTree node;
930 algorithm
931 47 (tokens, tree, b) := LA1(tokens, tree, {TokenId.REDECLARE});
932
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if b then
933 ✗ (tokens, tree, nodeName) := element_redeclaration(tokens, tree);
934 else
935 47 (tokens, tree, nodeName) := element_modification_or_replaceable(tokens, tree);
936 end if;
937 47 node := makeNode(listReverse(tree), label=nodeName);
938 outTree := node::inTree;
939 end argument;
940
941 function element_redeclaration
942 extends partialParser;
943 output ParseTree nodeName;
944 protected
945 Boolean b;
946 algorithm
947 ✗ (tokens, tree) := scan(tokens, tree, TokenId.REDECLARE);
948 ✗ (tokens, tree) := scanOpt(tokens, tree, TokenId.EACH);
949 ✗ (tokens, tree) := scanOpt(tokens, tree, TokenId.FINAL);
950 ✗ (tokens, tree, b) := LA1(tokens, tree, {TokenId.REPLACEABLE});
951 ✗ if b then
952 ✗ (tokens, tree, nodeName) := element_replaceable(tokens, tree);
953 else
954 ✗ (tokens, tree, b) := LA1(tokens, tree, First.class_prefixes);
955 ✗ if b then
956 ✗ (tokens, tree, nodeName) := short_class_definition(tokens, tree);
957 else
958 ✗ (tokens, tree, nodeName) := component_clause1(tokens, tree);
959 end if;
960 end if;
961 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
962 end element_redeclaration;
963
964 function short_class_definition
965 extends partialParser;
966 output ParseTree nodeName;
967 protected
968 algorithm
969 ✗ (tokens, tree) := class_prefixes(tokens, tree);
970 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
971 ✗ nodeName::_ := tree;
972 ✗ nodeName := parseTreeFilterWhitespace(nodeName);
973 ✗ (tokens, tree) := scan(tokens, tree, TokenId.EQUALS);
974 ✗ (tokens, tree) := short_class_specifier1(tokens, tree);
975 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
976 end short_class_definition;
977
978 function element_modification_or_replaceable
979 extends partialParser;
980 output ParseTree nodeName;
981 protected
982 Boolean b;
983 algorithm
984 47 (tokens, tree) := scanOpt(tokens, tree, TokenId.EACH);
985 47 (tokens, tree) := scanOpt(tokens, tree, TokenId.FINAL);
986 47 (tokens, tree, b) := LA1(tokens, tree, {TokenId.REPLACEABLE});
987
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if b then
988 ✗ (tokens, tree, nodeName) := element_replaceable(tokens, tree);
989 else
990 47 (tokens, tree, nodeName) := element_modification(tokens, tree);
991 end if;
992 47 outTree := makeNodePrependTree(listReverse(tree), inTree);
993 end element_modification_or_replaceable;
994
995 function element_replaceable
996 extends partialParser;
997 output ParseTree nodeName;
998 protected
999 Boolean b;
1000 algorithm
1001 ✗ (tokens, tree) := scan(tokens, tree, TokenId.REPLACEABLE);
1002 ✗ (tokens, tree, b) := LA1(tokens, tree, First.component_clause);
1003 ✗ if b then
1004 ✗ (tokens, tree, nodeName) := component_clause1(tokens, tree);
1005 else
1006 ✗ (tokens, tree, nodeName) := short_class_definition(tokens, tree);
1007 end if;
1008 ✗ (tokens, tree, b) := LA1(tokens, tree, {TokenId.CONSTRAINEDBY});
1009 ✗ if b then
1010 ✗ (tokens, tree) := constraining_clause(tokens, tree);
1011 ✗ (tokens, tree) := comment(tokens, tree); // Not standard Modelica, but some libraries have this
1012 end if;
1013 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
1014 end element_replaceable;
1015
1016 function element_modification
1017 extends partialParser;
1018 output ParseTree nodeName;
1019 protected
1020 Boolean b;
1021 algorithm
1022 47 (tokens, tree) := name(tokens, tree);
1023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 nodeName::_ := tree;
1024 47 nodeName := parseTreeFilterWhitespace(nodeName);
1025 47 (tokens, tree, b) := LA1(tokens, tree, First.modification);
1026
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if b then
1027 47 (tokens, tree) := modification(tokens, tree);
1028 end if;
1029 47 (tokens, tree) := string_comment(tokens, tree);
1030 47 outTree := makeNodePrependTree(listReverse(tree), inTree);
1031 end element_modification;
1032
1033 function modification
1034 extends partialParser;
1035 protected
1036 Boolean b;
1037 algorithm
1038 57 (tokens, tree, b) := LA1(tokens, tree, First.class_modification);
1039
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 37 times.
57 if b then
1040 20 (tokens, tree) := class_modification(tokens, tree);
1041 20 (tokens, tree) := eatWhitespace(tokens, tree);
1042 20 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.EQUALS);
1043 20 (tokens, tree) := eatWhitespace(tokens, tree);
1044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if b then
1045 ✗ (tokens, tree) := expression(tokens, tree);
1046 end if;
1047 else
1048 37 (tokens, tree) := eatWhitespace(tokens, tree);
1049 37 (tokens, tree) := scanOneOf(tokens, tree, {TokenId.EQUALS, TokenId.ASSIGN});
1050 37 (tokens, tree) := eatWhitespace(tokens, tree);
1051 37 (tokens, tree) := expression(tokens, tree);
1052 end if;
1053 57 outTree := makeNodePrependTree(listReverse(tree), inTree);
1054 end modification;
1055
1056 function expression_list
1057 extends partialParser;
1058 protected
1059 Boolean b;
1060 algorithm
1061 while true loop
1062 4 (tokens, tree) := expression(tokens, tree);
1063 4 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
1064
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if not b then
1065 break;
1066 end if;
1067 end while;
1068 2 outTree := makeNodePrependTree(listReverse(tree), inTree);
1069 end expression_list;
1070
1071 function expression
1072 extends partialParser;
1073 protected
1074 Boolean b;
1075 list<ParseTree> ifTrees = inTree;
1076 algorithm
1077 295 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.IF);
1078
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 291 times.
295 if b then
1079 4 (tokens, tree) := expression(tokens, tree);
1080 4 ifTrees := listAppend(makeNodePrependTree(listReverse(tree), {}, label=LEAF(makeToken(TokenId.IDENT, "$if_cond"))), ifTrees);
1081 4 tree := {};
1082
1083 4 (tokens, tree) := scan(tokens, tree, TokenId.THEN);
1084 4 (tokens, tree) := expression(tokens, tree);
1085 4 ifTrees := listAppend(makeNodePrependTree(listReverse(tree), {}, label=LEAF(makeToken(TokenId.IDENT, "$then"))), ifTrees);
1086 4 tree := {};
1087 ✗ while true loop
1088 4 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.ELSEIF);
1089
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if not b then
1090 break;
1091 end if;
1092 ✗ (tokens, tree) := expression(tokens, tree);
1093 ✗ (tokens, tree) := scan(tokens, tree, TokenId.THEN);
1094 ✗ (tokens, tree) := expression(tokens, tree);
1095 ✗ ifTrees := listAppend(makeNodePrependTree(listReverse(tree), {}, label=LEAF(makeToken(TokenId.IDENT, "$else_if"))), ifTrees);
1096 ✗ tree := {};
1097 end while;
1098 4 (tokens, tree) := scan(tokens, tree, TokenId.ELSE);
1099 4 (tokens, tree) := expression(tokens, tree);
1100 4 ifTrees := listAppend(makeNodePrependTree(listReverse(tree), {}, label=LEAF(makeToken(TokenId.IDENT, "$else"))), ifTrees);
1101 4 tree := {};
1102 4 outTree := makeNodePrependTree({}, ifTrees);
1103 4 return;
1104 end if;
1105 291 (tokens, tree) := simple_expression(tokens, tree);
1106 291 outTree := makeNodePrependTree(listReverse(tree), inTree);
1107 end expression;
1108
1109 function simple_expression
1110 extends partialParser;
1111 protected
1112 Boolean b;
1113 algorithm
1114 291 (tokens, tree) := logical_expression(tokens, tree);
1115 291 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COLON);
1116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 291 times.
291 if b then
1117 ✗ (tokens, tree) := logical_expression(tokens, tree);
1118 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COLON);
1119 ✗ if b then
1120 ✗ (tokens, tree) := logical_expression(tokens, tree);
1121 end if;
1122 end if;
1123 291 outTree := makeNodePrependTree(listReverse(tree), inTree);
1124 end simple_expression;
1125
1126 function logical_expression
1127 extends partialParser;
1128 protected
1129 Boolean b;
1130 algorithm
1131 291 (tokens, tree) := logical_term(tokens, tree);
1132 2 while true loop
1133 293 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.OR);
1134
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 291 times.
293 if not b then
1135 break;
1136 end if;
1137 2 (tokens, tree) := logical_term(tokens, tree);
1138 end while;
1139 291 outTree := makeNodePrependTree(listReverse(tree), inTree);
1140 end logical_expression;
1141
1142 function logical_term
1143 extends partialParser;
1144 protected
1145 Boolean b;
1146 algorithm
1147 293 (tokens, tree) := logical_factor(tokens, tree);
1148 ✗ while true loop
1149 293 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.AND);
1150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 293 times.
293 if not b then
1151 break;
1152 end if;
1153 ✗ (tokens, tree) := logical_factor(tokens, tree);
1154 end while;
1155 293 outTree := makeNodePrependTree(listReverse(tree), inTree);
1156 end logical_term;
1157
1158 function logical_factor
1159 extends partialParser;
1160 protected
1161 Boolean b;
1162 algorithm
1163 293 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.NOT);
1164 293 (tokens, tree) := relation(tokens, tree);
1165 293 outTree := makeNodePrependTree(listReverse(tree), inTree);
1166 end logical_factor;
1167
1168 function relation
1169 extends partialParser;
1170 protected
1171 Boolean b;
1172 constant list<TokenId> rel_op = {TokenId.LESS, TokenId.LESSEQ, TokenId.GREATER, TokenId.GREATEREQ, TokenId.EQEQ, TokenId.LESSGT};
1173 algorithm
1174 293 (tokens, tree) := arithmetic_expression(tokens, tree);
1175 30 while true loop
1176 323 (tokens, tree, b) := LA1(tokens, tree, rel_op, consume=true);
1177
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 293 times.
323 if not b then
1178 break;
1179 end if;
1180 30 (tokens, tree) := arithmetic_expression(tokens, tree);
1181 end while;
1182 293 outTree := makeNodePrependTree(listReverse(tree), inTree);
1183 end relation;
1184
1185 function arithmetic_expression
1186 extends partialParser;
1187 protected
1188 Boolean b;
1189 constant list<TokenId> add_op = {TokenId.PLUS, TokenId.MINUS, TokenId.PLUS_EW, TokenId.MINUS_EW};
1190 algorithm
1191 323 (tokens, tree) := LA1(tokens, tree, add_op, consume=true);
1192 323 (tokens, tree) := term(tokens, tree);
1193 8 while true loop
1194 331 (tokens, tree, b) := LA1(tokens, tree, add_op, consume=true);
1195
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 323 times.
331 if not b then
1196 break;
1197 end if;
1198 8 (tokens, tree) := term(tokens, tree);
1199 end while;
1200 323 outTree := makeNodePrependTree(listReverse(tree), inTree);
1201 end arithmetic_expression;
1202
1203 function term
1204 extends partialParser;
1205 protected
1206 Boolean b;
1207 constant list<TokenId> mul_op = {TokenId.STAR, TokenId.STAR_EW, TokenId.SLASH, TokenId.SLASH_EW};
1208 algorithm
1209 331 (tokens, tree) := factor(tokens, tree);
1210 12 while true loop
1211 343 (tokens, tree, b) := LA1(tokens, tree, mul_op, consume=true);
1212
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 331 times.
343 if not b then
1213 break;
1214 end if;
1215 12 (tokens, tree) := factor(tokens, tree);
1216 end while;
1217 331 outTree := makeNodePrependTree(listReverse(tree), inTree);
1218 end term;
1219
1220 function factor
1221 extends partialParser;
1222 protected
1223 Boolean b;
1224 constant list<TokenId> pow_op = {TokenId.POWER, TokenId.POWER_EW};
1225 algorithm
1226 343 (tokens, tree) := primary(tokens, tree);
1227 ✗ while true loop
1228 343 (tokens, tree, b) := LA1(tokens, tree, pow_op, consume=true);
1229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 343 times.
343 if not b then
1230 break;
1231 end if;
1232 ✗ (tokens, tree) := primary(tokens, tree);
1233 end while;
1234 343 outTree := makeNodePrependTree(listReverse(tree), inTree);
1235 end factor;
1236
1237 function primary
1238 extends partialParser;
1239 protected
1240 TokenId id;
1241 Boolean b;
1242 String label = "expression";
1243 algorithm
1244 343 (tokens, tree, b) := LA1(tokens, tree, {TokenId.UNSIGNED_INTEGER, TokenId.UNSIGNED_REAL, TokenId.FALSE, TokenId.TRUE, TokenId.END, TokenId.STRING});
1245
2/2
✓ Branch 0 taken 181 times.
✓ Branch 1 taken 162 times.
343 if b then
1246 181 (tokens, tree) := consume(tokens, tree);
1247 181 outTree := makeNodePrependTree(listReverse(tree), inTree);
1248 181 return;
1249 end if;
1250 162 (tokens, tree, id) := peek(tokens, tree);
1251
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 156 times.
162 if id==TokenId.LPAR then
1252 6 (tokens, tree) := output_expression_list(tokens, tree);
1253 label := "$parenthesis";
1254 elseif id==TokenId.LBRACE then
1255 54 (tokens, tree) := scan(tokens, tree, TokenId.LBRACE);
1256 54 (tokens, tree, b) := LA1(tokens, tree, {TokenId.RBRACE}); // Easier than checking First(expression), etc
1257
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 if not b then
1258 54 (tokens, tree) := function_arguments(tokens, tree);
1259 end if;
1260 54 (tokens, tree) := scan(tokens, tree, TokenId.RBRACE);
1261 label := "$array";
1262 elseif id==TokenId.LBRACK then
1263 2 (tokens, tree) := consume(tokens, tree);
1264 2 (tokens, tree) := expression_list(tokens, tree);
1265 ✗ while true loop
1266 2 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.SEMICOLON);
1267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if not b then
1268 break;
1269 end if;
1270 ✗ (tokens, tree) := expression_list(tokens, tree);
1271 end while;
1272 2 (tokens, tree) := scan(tokens, tree, TokenId.RBRACK);
1273 label := "$matrix";
1274 elseif listMember(id, {TokenId.DER, TokenId.INITIAL}) then
1275 ✗ (tokens, tree) := consume(tokens, tree);
1276 ✗ (tokens, tree, b) := LA1(tokens, tree, {TokenId.LPAR});
1277 ✗ if b then
1278 ✗ (tokens, tree) := function_call_args(tokens, tree);
1279 end if;
1280 label := "$initial";
1281 elseif listMember(id, {TokenId.DOT, TokenId.IDENT, TokenId.FUNCTION}) then
1282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if id == TokenId.FUNCTION then
1283 // Function partial applications sort of looks like a normal call
1284 ✗ (tokens, tree) := consume(tokens, tree);
1285 end if;
1286 100 (tokens, tree) := component_reference(tokens, tree);
1287 100 (tokens, tree, b) := LA1(tokens, tree, {TokenId.LPAR});
1288
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 80 times.
100 if b then
1289 20 (tokens, tree) := function_call_args(tokens, tree);
1290 label := "$call";
1291 end if;
1292 else
1293 ✗ error(tokens, tree, {});
1294 end if;
1295 162 outTree := makeNodePrependTree(listReverse(tree), inTree, label=LEAF(makeToken(TokenId.IDENT, label)));
1296 end primary;
1297
1298 function function_call_args
1299 extends partialParser;
1300 protected
1301 Boolean b;
1302 algorithm
1303 22 (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
1304 22 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.RPAR); // Easier than checking First.expression, etc
1305 22 (tokens, tree) := eatWhitespace(tokens, tree);
1306
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if not b then
1307 22 (tokens, tree) := function_arguments(tokens, tree);
1308 22 (tokens, tree) := scan(tokens, tree, TokenId.RPAR);
1309 end if;
1310 22 (tokens, tree) := eatWhitespace(tokens, tree);
1311 22 outTree := makeNodePrependTree(listReverse(tree), inTree);
1312 end function_call_args;
1313
1314 function function_arguments
1315 extends partialParser;
1316 protected
1317 Boolean b;
1318 list<ParseTree> tree2;
1319 list<list<ParseTree>> trees;
1320 algorithm
1321 trees := {};
1322 66 while true loop
1323 142 (tokens, tree, b) := LAk(tokens, tree, {{TokenId.IDENT}, {TokenId.EQUALS}});
1324
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 132 times.
142 if b then
1325 10 (tokens, tree) := named_arguments(tokens, tree);
1326 10 trees := tree :: trees;
1327 10 tree := {};
1328 10 break;
1329 else
1330 132 (tokens, tree) := function_argument(tokens, tree);
1331 132 (tokens, tree2, b) := scanOpt(tokens, {}, TokenId.COMMA);
1332
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 66 times.
132 if b then
1333 66 (tokens, tree2) := eatWhitespace(tokens, tree2);
1334 end if;
1335
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 66 times.
132 if b then
1336 132 tree := makeNode(listReverse(tree2))::tree;
1337 else
1338 66 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.FOR);
1339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if b then
1340 ✗ (tokens, tree) := for_indices(tokens, tree);
1341 end if;
1342 66 trees := tree :: trees;
1343 66 tree := {};
1344 66 break;
1345 end if;
1346 end if;
1347 end while;
1348 outTree := inTree;
1349
2/2
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 76 times.
152 for tree in listReverse(trees) loop
1350 76 outTree := makeNodePrependTree(listReverse(tree), outTree, label=LEAF(makeToken(TokenId.IDENT, "function_arguments")));
1351 end for;
1352 end function_arguments;
1353
1354 function function_argument
1355 extends partialParser;
1356 protected
1357 Boolean b;
1358 algorithm
1359 132 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.FUNCTION);
1360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
132 if b then
1361 ✗ (tokens, tree) := name(tokens, tree);
1362 ✗ (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
1363 ✗ (tokens, tree, b) := LA1(tokens, tree, {TokenId.IDENT});
1364 ✗ if b then
1365 ✗ (tokens, tree) := named_arguments(tokens, tree);
1366 end if;
1367 ✗ (tokens, tree) := scan(tokens, tree, TokenId.RPAR);
1368 else
1369 132 (tokens, tree) := expression(tokens, tree);
1370 end if;
1371 132 outTree := makeNodePrependTree(listReverse(tree), inTree);
1372 end function_argument;
1373
1374 function named_arguments
1375 extends partialParser;
1376 protected
1377 Boolean b;
1378 algorithm
1379 10 (tokens, tree) := named_argument(tokens, tree);
1380 10 while true loop
1381 20 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
1382
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
20 if not b then
1383 break;
1384 end if;
1385 10 (tokens, tree) := named_argument(tokens, tree);
1386 end while;
1387 10 outTree := makeNodePrependTree(listReverse(tree), inTree);
1388 end named_arguments;
1389
1390 function named_argument
1391 extends partialParser;
1392 protected
1393 ParseTree label;
1394 algorithm
1395 20 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
1396 20 label := listHead(tree);
1397 20 (tokens, tree) := scan(tokens, tree, TokenId.EQUALS);
1398 20 (tokens, tree) := expression(tokens, tree);
1399 20 outTree := makeNodePrependTree(listReverse(tree), inTree, label=label);
1400 end named_argument;
1401
1402 function for_indices
1403 extends partialParser;
1404 protected
1405 Boolean b;
1406 algorithm
1407 ✗ (tokens, tree) := for_index(tokens, tree);
1408 ✗ while true loop
1409 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.COMMA);
1410 ✗ if not b then
1411 break;
1412 end if;
1413 ✗ (tokens, tree) := for_index(tokens, tree);
1414 end while;
1415 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
1416 end for_indices;
1417
1418 function for_index
1419 extends partialParser;
1420 protected
1421 Boolean b;
1422 algorithm
1423 ✗ (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
1424 ✗ (tokens, tree, b) := scanOpt(tokens, tree, TokenId.IN);
1425 ✗ if b then
1426 ✗ (tokens, tree) := expression(tokens, tree);
1427 end if;
1428 ✗ outTree := makeNodePrependTree(listReverse(tree), inTree);
1429 end for_index;
1430
1431 function string_comment
1432 extends partialParser;
1433 protected
1434 Boolean b;
1435 algorithm
1436 300 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.STRING);
1437
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 300 times.
316 while b loop
1438 16 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.PLUS);
1439
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if b then
1440 ✗ (tokens, tree) := scan(tokens, tree, TokenId.STRING);
1441 end if;
1442 end while;
1443 300 outTree := makeNodePrependTree(listReverse(tree), inTree);
1444 end string_comment;
1445
1446 function output_expression_list
1447 extends partialParser;
1448 protected
1449 Boolean b1, b2;
1450 algorithm
1451 6 (tokens, tree) := scan(tokens, tree, TokenId.LPAR);
1452 while true loop
1453 12 (tokens, tree, b1) := scanOpt(tokens, tree, TokenId.COMMA);
1454 12 (tokens, tree, b2) := scanOpt(tokens, tree, TokenId.RPAR);
1455
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if b2 then
1456 break;
1457 end if;
1458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if not b1 then
1459 6 (tokens, tree) := expression(tokens, tree);
1460 end if;
1461 end while;
1462 6 outTree := makeNodePrependTree(listReverse(tree), inTree);
1463 end output_expression_list;
1464
1465 function name
1466 extends partialParser;
1467 protected
1468 Boolean b;
1469 algorithm
1470 121 (tokens, tree) := scanOpt(tokens, tree, TokenId.DOT);
1471 121 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
1472 17 while true loop
1473 138 (tokens, tree, b) := LAk(tokens, tree, {{TokenId.DOT}, {TokenId.IDENT}});
1474
3/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
138 if not b then
1475 break;
1476 end if;
1477 17 (tokens, tree) := scan(tokens, tree, TokenId.DOT);
1478 17 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
1479 end while;
1480 121 outTree := makeNodePrependTree(listReverse(tree), inTree);
1481 end name;
1482
1483 function component_reference
1484 extends partialParser;
1485 protected
1486 Boolean b;
1487 algorithm
1488 156 (tokens, tree) := scanOpt(tokens, tree, TokenId.DOT);
1489 while true loop
1490 234 (tokens, tree) := scan(tokens, tree, TokenId.IDENT);
1491 234 (tokens, tree, b) := LA1(tokens, tree, {TokenId.LBRACK});
1492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if b then
1493 ✗ (tokens, tree) := array_subscripts(tokens, tree);
1494 end if;
1495 234 (tokens, tree, b) := scanOpt(tokens, tree, TokenId.DOT);
1496
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 156 times.
234 if not b then
1497 break;
1498 end if;
1499 end while;
1500 156 outTree := makeNodePrependTree(listReverse(tree), inTree);
1501 end component_reference;
1502
1503 function comment
1504 extends partialParser;
1505 protected
1506 Boolean b;
1507 algorithm
1508 155 (tokens, tree) := string_comment(tokens, tree);
1509 155 (tokens, tree, b) := LA1(tokens, tree, First._annotation);
1510
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 150 times.
155 if b then
1511 5 (tokens, tree) := _annotation(tokens, tree);
1512 end if;
1513 155 outTree := makeNodePrependTree(listReverse(tree), inTree);
1514 end comment;
1515
1516 function _annotation
1517 extends partialParser;
1518 protected
1519 algorithm
1520 tree := {};
1521 7 (tokens, tree) := scan(tokens, tree, TokenId.ANNOTATION);
1522 7 (tokens, tree) := class_modification(tokens, tree);
1523 7 outTree := makeNode(listReverse(tree), label=LEAF(makeToken(TokenId.IDENT, "annotation")))::inTree;
1524 end _annotation;
1525
1526 protected
1527
1528 function findWithin
1529 input list<ParseTree> tree;
1530 output ParseTree w=EMPTY();
1531 protected
1532 Token tok, tok2;
1533 list<ParseTree> rest, rest2;
1534 algorithm
1535 w := match tree
1536 case NODE(label=LEAF(token=tok), nodes=(w as NODE(label=LEAF(token=tok2)))::rest)::rest2 guard tokenContent(tok)=="$program" and tokenContent(tok2)=="$within"
1537 then w;
1538 else EMPTY();
1539 end match;
1540 end findWithin;
1541
1542 function moveComments
1543 input list<ParseTree> t1;
1544 input output list<ParseTree> t2;
1545 protected
1546 list<tuple<Token, list<ParseTree>, String>> c1, c2;
1547 Token tok;
1548 String str1,str2;
1549 list<ParseTree> path1, path2, tempTree;
1550 algorithm
1551 // TODO: Collect all comments (in order), diff all comments, walk t1/t2 to mark all of the comments as significant or not...
1552 // TODO: OR? Look for moved comments and try to detect if preceeded / succeeded by the same tokens in the same label
1553 ✗ c1 := findCommentsWithLabels(t1, {}, {});
1554 ✗ c2 := findCommentsWithLabels(t2, {}, {});
1555 ✗ (_,c1,c2) := List.intersection1OnTrue(c1, c2, foundCommentEqual);
1556 ✗ for c in c2 loop
1557 try
1558 ✗ (tok,path1,str1) := c;
1559 ✗ ((_,path2,str2), c1) := List.findAndRemove1(c1, foundCommentTokenEqual, c);
1560 ✗ (tempTree, true) := removeCommentAtLabelPath(t2, tok, listReverse(path1));
1561 ✗ (tempTree, true) := addCommentAtLabelPath(tempTree, tok, listReverse(path2));
1562 t2 := tempTree;
1563 else
1564 end try;
1565 end for;
1566 end moveComments;
1567
1568 function moveCommentsAfterDiff
1569 input output list<tuple<Diff,list<ParseTree>>> res;
1570 protected
1571 String foundComment;
1572 ParseTree tree, foundTree;
1573 list<ParseTree> trees, before, after, acc2;
1574 AvlSetString.Tree comments;
1575 list<tuple<Diff,list<ParseTree>>> acc, lst;
1576 tuple<Diff,list<ParseTree>> diff;
1577 Boolean found;
1578 algorithm
1579 // O(N*D); scales with number of diffs since we restart whenever we move a comment
1580 ✗ comments := findAddedComments(res);
1581 acc := {};
1582 lst := res;
1583 ✗ if AvlSetString.isEmpty(comments) then
1584 ✗ return;
1585 end if;
1586 ✗ while not listEmpty(lst) loop
1587 ✗ diff::lst := lst;
1588 () := match diff
1589 case (Diff.Delete, trees)
1590 algorithm
1591 acc2 := {};
1592 ✗ while not listEmpty(trees) loop
1593 ✗ tree::trees := trees;
1594 ✗ (found,before,foundTree,after,foundComment) := fixDeletedComments(tree, comments);
1595 ✗ if found then
1596 ✗ acc := (Diff.Delete, listAppend(listReverse(acc2),before))::acc;
1597 ✗ res := listAppend(listReverse(acc),(Diff.Equal,{foundTree})::(Diff.Delete, listAppend(after, trees))::lst);
1598 ✗ print(DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr) + "\n");
1599 ✗ res := removeAddedCommentFromDiff(res, foundComment);
1600 // Continue until we can't fix more comments
1601 res := moveCommentsAfterDiff(res);
1602 ✗ return;
1603 end if;
1604 acc2 := tree::acc2;
1605 end while;
1606 then ();
1607 else ();
1608 end match;
1609 acc := diff::acc;
1610 end while;
1611 end moveCommentsAfterDiff;
1612
1613 function findAddedComments
1614 input list<tuple<Diff,list<ParseTree>>> tree;
1615 output AvlSetString.Tree comments = AvlSetString.EMPTY();
1616 protected
1617 list<ParseTree> addedTrees;
1618 algorithm
1619 ✗ (addedTrees,) := extractAdditionsDeletions(tree);
1620 ✗ for t in addedTrees loop
1621 ✗ comments := findAddedComments2(t, comments);
1622 end for;
1623 end findAddedComments;
1624
1625 function findAddedComments2
1626 input ParseTree tree;
1627 input output AvlSetString.Tree comments;
1628 protected
1629 list<ParseTree> nodes;
1630 algorithm
1631 comments := match tree
1632 ✗ case LEAF() guard parseTreeIsComment(tree) then AvlSetString.add(comments, tokenContent(tree.token));
1633 case NODE(nodes=nodes)
1634 algorithm
1635 ✗ for n in nodes loop
1636 ✗ comments := findAddedComments2(n, comments);
1637 end for;
1638 then comments;
1639 else comments;
1640 end match;
1641 end findAddedComments2;
1642
1643 function removeAddedCommentFromDiff
1644 input output list<tuple<Diff,list<ParseTree>>> tree;
1645 input String comment;
1646 protected
1647 list<tuple<Diff,list<ParseTree>>> acc, lst;
1648 tuple<Diff,list<ParseTree>> diff;
1649 list<ParseTree> lst2;
1650 Boolean b;
1651 algorithm
1652 lst := tree;
1653 acc := {};
1654 ✗ while not listEmpty(lst) loop
1655 ✗ diff::lst := lst;
1656 () := match diff
1657 case (Diff.Add,lst2)
1658 algorithm
1659 ✗ (b,lst2) := removeAddedCommentFromDiff2(lst2, comment);
1660 ✗ if b then
1661 ✗ tree := listAppend(listReverse(acc),(Diff.Add,lst2)::lst);
1662 ✗ return;
1663 end if;
1664 then ();
1665 else ();
1666 end match;
1667 acc := diff::acc;
1668 end while;
1669 ✗ Error.addInternalError("Failed to remove comment `"+comment+"` from diff; but we know it is in there somewhere", sourceInfo());
1670 end removeAddedCommentFromDiff;
1671
1672 function removeAddedCommentFromDiff2
1673 output Boolean removed=false;
1674 input output list<ParseTree> trees;
1675 input String comment;
1676 protected
1677 list<ParseTree> acc, lst, nodes;
1678 ParseTree tree;
1679 String content;
1680 algorithm
1681 acc := {};
1682 lst := trees;
1683 ✗ while not listEmpty(lst) loop
1684 ✗ tree::lst := lst;
1685 (removed, tree) := match tree
1686 case LEAF() guard parseTreeIsComment(tree)
1687 algorithm
1688 ✗ content := tokenContent(tree.token);
1689 ✗ then (content==comment, if content==comment then EMPTY() else tree);
1690 case NODE(nodes=nodes)
1691 algorithm
1692 ✗ (removed, nodes) := removeAddedCommentFromDiff2(nodes, comment);
1693 ✗ if removed then
1694 ✗ tree.nodes := nodes;
1695 end if;
1696 then (removed, tree);
1697 else (false, tree);
1698 end match;
1699 ✗ if removed then
1700 ✗ lst := if isEmpty(tree) then lst else (tree::lst);
1701 ✗ lst := listAppend(listReverse(acc), lst);
1702 trees := lst;
1703 ✗ return;
1704 end if;
1705 acc := tree::acc;
1706 end while;
1707 end removeAddedCommentFromDiff2;
1708
1709 function fixDeletedComments
1710 input ParseTree tree;
1711 input AvlSetString.Tree addedComments;
1712 output Boolean found = false;
1713 output list<ParseTree> before = {};
1714 output ParseTree foundTree = tree;
1715 output list<ParseTree> after = {};
1716 output String foundComment = "";
1717 protected
1718 list<ParseTree> nodes, before2, after2;
1719 Boolean b;
1720 ParseTree t;
1721 String content;
1722 algorithm
1723 found := match tree
1724 case LEAF() guard parseTreeIsComment(tree)
1725 algorithm
1726 ✗ content := tokenContent(tree.token);
1727 ✗ b := AvlSetString.hasKey(addedComments, content);
1728 ✗ if b then
1729 ✗ foundComment := content;
1730 end if;
1731 then b;
1732 case NODE(nodes=nodes)
1733 algorithm
1734 ✗ while not listEmpty(nodes) loop
1735 ✗ t::nodes := nodes;
1736 ✗ (found, before2, foundTree, after2, foundComment) := fixDeletedComments(t, addedComments);
1737 ✗ if found then
1738 ✗ before := listAppend(listReverse(before), before2);
1739 ✗ after := listAppend(after2, nodes);
1740 ✗ return;
1741 end if;
1742 before := t::before;
1743 end while;
1744 before := {};
1745 ✗ foundTree := tree;
1746 after := {};
1747 ✗ foundComment := "";
1748 then false;
1749 else false;
1750 end match;
1751 end fixDeletedComments;
1752
1753 function addCommentAtLabelPath
1754 input output list<ParseTree> tree;
1755 input Token tok;
1756 input list<ParseTree> path;
1757 output Boolean success=false;
1758 protected
1759 ParseTree n, n2, label, pathFirst;
1760 list<ParseTree> rest, nodes, pathRest;
1761 DoubleEnded.MutableList<ParseTree> delst;
1762 Boolean b;
1763 algorithm
1764 ✗ if listEmpty(path) then
1765 success := true;
1766 ✗ tree := LEAF(tok)::tree;
1767 ✗ return;
1768 end if;
1769 ✗ delst := DoubleEnded.fromList({});
1770 rest := tree;
1771 ✗ while not listEmpty(rest) loop
1772 ✗ n::rest := rest;
1773 (n2,b) := match (n,path)
1774 case (NODE(label=EMPTY()),_)
1775 algorithm
1776 ✗ (nodes,b) := addCommentAtLabelPath(n.nodes,tok,path);
1777 ✗ if b then
1778 ✗ n2 := NODE(EMPTY(),nodes);
1779 else
1780 n2 := n;
1781 end if;
1782 ✗ then (n2,b);
1783 case (NODE(label=label),pathFirst::pathRest) guard stringEq(labelPathStr({label}), labelPathStr({pathFirst}))
1784 algorithm
1785 ✗ (nodes,b) := addCommentAtLabelPath(n.nodes,tok,pathRest);
1786 ✗ if b then
1787 ✗ n2 := NODE(label,nodes);
1788 else
1789 n2 := n;
1790 end if;
1791 ✗ then (n2,b);
1792 else (n,false);
1793 end match;
1794 ✗ DoubleEnded.push_back(delst, n2);
1795 ✗ if b then
1796 ✗ tree := DoubleEnded.toListAndClear(delst, prependToList=rest);
1797 success := true;
1798 ✗ return;
1799 end if;
1800 end while;
1801 // return tree as it is
1802 end addCommentAtLabelPath;
1803
1804 function removeCommentAtLabelPath
1805 input output list<ParseTree> tree;
1806 input Token tok;
1807 input list<ParseTree> path;
1808 output Boolean success=false;
1809 protected
1810 ParseTree n, n2, label, pathFirst;
1811 list<ParseTree> rest, nodes, pathRest;
1812 DoubleEnded.MutableList<ParseTree> delst;
1813 Boolean b;
1814 algorithm
1815 ✗ if listEmpty(path) then
1816 ✗ (tree,success as true) := removeCommentAtThisLabel(tree, tok);
1817 ✗ return;
1818 end if;
1819 ✗ delst := DoubleEnded.fromList({});
1820 rest := tree;
1821 ✗ while not listEmpty(rest) loop
1822 ✗ n::rest := rest;
1823 (n2,b) := match (n,path)
1824 case (NODE(label=EMPTY()),_)
1825 algorithm
1826 ✗ (nodes,b) := removeCommentAtLabelPath(n.nodes,tok,path);
1827 ✗ if b then
1828 ✗ n2 := NODE(EMPTY(),nodes);
1829 else
1830 n2 := n;
1831 end if;
1832 ✗ then (n2,b);
1833 case (NODE(label=label),pathFirst::pathRest) guard stringEq(labelPathStr({label}), labelPathStr({pathFirst}))
1834 algorithm
1835 ✗ (nodes,b) := removeCommentAtLabelPath(n.nodes,tok,pathRest);
1836 ✗ if b then
1837 ✗ n2 := NODE(label,nodes);
1838 else
1839 n2 := n;
1840 end if;
1841 ✗ then (n2,b);
1842 else (n,false);
1843 end match;
1844 ✗ DoubleEnded.push_back(delst, n2);
1845 ✗ if b then
1846 ✗ tree := DoubleEnded.toListAndClear(delst, prependToList=rest);
1847 success := true;
1848 ✗ return;
1849 end if;
1850 end while;
1851 // return tree as it is
1852 end removeCommentAtLabelPath;
1853
1854 function removeCommentAtThisLabel
1855 input output list<ParseTree> tree;
1856 input Token tok;
1857 output Boolean success=false;
1858 protected
1859 DoubleEnded.MutableList<ParseTree> delst;
1860 list<ParseTree> rest=tree, nodes;
1861 ParseTree n;
1862 algorithm
1863 ✗ delst := DoubleEnded.fromList({});
1864 ✗ while not listEmpty(rest) loop
1865 ✗ n::rest := rest;
1866 () := match n
1867 case LEAF() guard modelicaDiffTokenEq(n.token, tok)
1868 algorithm
1869 ✗ success := true;
1870 ✗ tree := DoubleEnded.toListAndClear(delst, prependToList=rest);
1871 ✗ return;
1872 then fail();
1873 case NODE(label=EMPTY())
1874 algorithm
1875 ✗ (nodes,success) := removeCommentAtThisLabel(n.nodes, tok);
1876 ✗ if success then
1877 ✗ DoubleEnded.push_back(delst, NODE(EMPTY(), nodes));
1878 ✗ tree := DoubleEnded.toListAndClear(delst, prependToList=rest);
1879 ✗ return;
1880 end if;
1881 then ();
1882 else ();
1883 end match;
1884 ✗ DoubleEnded.push_back(delst, n);
1885 end while;
1886 end removeCommentAtThisLabel;
1887
1888 function findCommentsWithLabels
1889 input list<ParseTree> t1;
1890 input list<ParseTree> labelPath;
1891 input output list<tuple<Token, list<ParseTree>, String>> acc;
1892 protected
1893 list<ParseTree> nodes;
1894 Token tok;
1895 TokenId id;
1896 String pathStr;
1897 algorithm
1898 ✗ for n in t1 loop
1899 () := match n
1900 case EMPTY() then ();
1901 case LEAF(token=tok as LexerModelicaDiff.TOKEN(id=id)) guard parseTreeIsComment(n)
1902 algorithm
1903 ✗ pathStr := labelPathStr(labelPath);
1904 ✗ acc := (tok, labelPath, pathStr)::acc;
1905 then ();
1906 case NODE(label=EMPTY(), nodes=nodes)
1907 algorithm
1908 ✗ acc := findCommentsWithLabels(nodes, labelPath, acc);
1909 then ();
1910 case NODE(nodes=nodes)
1911 algorithm
1912 ✗ acc := findCommentsWithLabels(nodes, n.label::labelPath, acc);
1913 then ();
1914 else ();
1915 end match;
1916 end for;
1917 end findCommentsWithLabels;
1918
1919 function foundCommentEqual
1920 input tuple<Token, list<ParseTree>, String> c1, c2;
1921 output Boolean eq;
1922 protected
1923 Token tok1, tok2;
1924 String s1, s2;
1925 algorithm
1926 ✗ (tok1,_,s1) := c1;
1927 ✗ (tok2,_,s2) := c2;
1928 ✗ eq := modelicaDiffTokenEq(tok1, tok2);
1929 ✗ if not eq then
1930 ✗ return;
1931 end if;
1932 ✗ eq := stringEq(s1, s2);
1933 end foundCommentEqual;
1934
1935 function foundCommentTokenEqual
1936 input tuple<Token, list<ParseTree>, String> c1, c2;
1937 output Boolean eq;
1938 protected
1939 Token tok1, tok2;
1940 algorithm
1941 ✗ (tok1,_,_) := c1;
1942 ✗ (tok2,_,_) := c2;
1943 ✗ eq := modelicaDiffTokenEq(tok1, tok2);
1944 end foundCommentTokenEqual;
1945
1946 function labelPathStr
1947 input list<ParseTree> labelPath;
1948 output String str;
1949 algorithm
1950 ✗ str := stringDelimitList(listReverse(parseTreeStr({t}) for t in labelPath), ".");
1951 end labelPathStr;
1952
1953 function treeDiffWork1
1954 input list<ParseTree> t1, t2;
1955 input Integer nTokens "The number of tokens in the larger tree; used to allocate arrays. Should be enough with the smaller tree, but there are no additional bounds checks this way.";
1956 output list<tuple<Diff,list<ParseTree>>> res;
1957 protected
1958 array<Token> diffSubtreeWorkArray1, diffSubtreeWorkArray2 "Used to handle diff of trees without using stack space or new allocations for every step";
1959 algorithm
1960 // Handle empty input
1961
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if listEmpty(t1) then
1962 ✗ res := {(Diff.Add, t2)};
1963 ✗ return;
1964 elseif listEmpty(t2) then
1965 ✗ res := {(Diff.Delete, t1)};
1966 ✗ return;
1967 end if;
1968 46 diffSubtreeWorkArray1 := MetaModelica.Dangerous.arrayCreateNoInit(nTokens, LexerModelicaDiff.noToken);
1969 46 diffSubtreeWorkArray2 := MetaModelica.Dangerous.arrayCreateNoInit(nTokens, LexerModelicaDiff.noToken);
1970
2/2
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 21 times.
46 if parseTreeEq(makeNode(t1), makeNode(t2), diffSubtreeWorkArray1=diffSubtreeWorkArray1, diffSubtreeWorkArray2=diffSubtreeWorkArray2) then
1971 // We need to do one check here since we assume the trees are different in the diff algorithm...
1972 25 res := {(Diff.Equal, t1)};
1973 25 return;
1974 end if;
1975 21 res := treeDiffWork(t1, t2, 1, function parseTreeEq(diffSubtreeWorkArray1=diffSubtreeWorkArray1, diffSubtreeWorkArray2=diffSubtreeWorkArray2));
1976 end treeDiffWork1;
1977
1978 function treeDiffWork
1979 input list<ParseTree> t1, t2;
1980 input Integer depth;
1981 input CmpParseTreeFunc compare;
1982 output list<tuple<Diff,list<ParseTree>>> res;
1983 protected
1984 list<tuple<Diff,list<ParseTree>>> resLocal;
1985 list<ParseTree> t2_strip, before, middle, after, addedTrees, deletedTrees, ts;
1986 list<String> addList = {}, delList = {};
1987 Integer nadd, ndel;
1988 ParseTree addedTree, deletedTree, deleted = EMPTY();
1989 Boolean addedBeforeDeleted, joinTrees, tryFind;
1990 String str, debugString1="", debugString2="";
1991 Diff d;
1992 algorithm
1993 // Speed-up. No deep compare for single nodes...
1994 () := match (t1, t2)
1995 case ({NODE(nodes=before)}, {NODE(nodes=after)})
1996 algorithm
1997 31 res := treeDiffWork(before, after, depth, compare);
1998 31 return;
1999 then ();
2000 case ({NODE(nodes=before)}, _)
2001 algorithm
2002 8 res := treeDiffWork(before, t2, depth, compare);
2003 8 return;
2004 then ();
2005 case (_, {NODE(nodes=after)})
2006 algorithm
2007 8 res := treeDiffWork(t1, after, depth, compare);
2008 8 return;
2009 then ();
2010 else ();
2011 end match;
2012
2/2
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 131 times.
151 if parseTreeIsNewLine(listHead(t2)) then
2013 20 t2_strip := listRest(t2);
2014 else
2015 t2_strip := t2;
2016 end if;
2017 if debug then
2018 print("Do diff at depth="+String(depth)+", len(t1)="+String(listLength(t1))+", len(t2)="+String(listLength(t2))+"\n");
2019 print("top t1="+firstTokenDebugStr(t1)+"\n");
2020 print("top t2="+firstTokenDebugStr(t2)+"\n");
2021 print("all t1="+parseTreeStr(t1)+"\n");
2022 print("all t2="+parseTreeStr(t2)+"\n");
2023 end if;
2024 151 res := diff(t1, t2, compare, parseTreeIsWhitespace, parseTreeIsWhitespaceNotComment, parseTreeNodeStr);
2025 151 (nadd, ndel) := countDiffAddDelete(res);
2026
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 139 times.
151 if nadd > 1 then
2027 12 res := fixMoveOperations(res, compare);
2028 12 (nadd, ndel) := countDiffAddDelete(res);
2029 end if;
2030 151 res := filterDiffWhitespace(res);
2031 if debug then
2032 print("nadd: " + String(nadd) + " ndel: " + String(ndel) + "\n");
2033 print(DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr) + "\n");
2034 if nadd <> ndel then
2035 for r in res loop
2036 (d,ts) := r;
2037 if d==Diff.Equal then
2038 continue;
2039 end if;
2040 for t in ts loop
2041 if isLabeledNode(t) then
2042 print(String(d) + " " + parseTreeStr(nodeLabel(t)::{})+"\n");
2043 end if;
2044 end for;
2045 end for;
2046 end if;
2047 end if;
2048
5/6
✓ Branch 0 taken 151 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 137 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 128 times.
✓ Branch 5 taken 9 times.
151 if depth>300 then
2049 // Do nothing; it's a diff... Just not perfect and might be really slow to process...
2050 elseif nadd==1 and ndel==1 then
2051 128 (addedTree, deletedTree, before, middle, after, addedBeforeDeleted) := extractSingleAddDiffBeforeAndAfter(res);
2052
7/8
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 8 times.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
144 if if not listEmpty(middle) then min(parseTreeIsWhitespace(middleItem) for middleItem in middle) else false then
2053 // If we have a change with whitespace in-between the added/deleted
2054 // items, move it so the added/deleted are next to each other to
2055 // merge them better.
2056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if addedBeforeDeleted then
2057 ✗ before := listAppend(before, middle) annotation(__OpenModelica_DisableListAppendWarning=true);
2058 else
2059 8 after := listAppend(middle, after);
2060 end if;
2061 8 middle := {};
2062 end if;
2063 // print("Doing tree diff on selected 1 addition + 1 deletion\n");
2064 // print("Added tree:"+parseTreeNodeStr(addedTree)+"\n");
2065 // print("Deleted tree:"+parseTreeNodeStr(deletedTree)+"\n");
2066 joinTrees := true;
2067
2/4
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 128 times.
128 if compare(addedTree, deletedTree) then
2068 // This is just a move operation; preserve whitespace
2069 ✗ res := {(Diff.Equal,{deletedTree})};
2070 elseif isLeaf(deletedTree) and isLeaf(addedTree) then
2071 res := res;
2072 joinTrees := false;
2073 elseif listEmpty(before) and listEmpty(after) then
2074 if debug then
2075 print("before and after empty\n");
2076 end if;
2077 res := res;
2078 else
2079 119 res := treeDiffWork(getNodes(deletedTree), getNodes(addedTree), depth+1, compare);
2080 end if;
2081 if not joinTrees then
2082 res := res;
2083 if debug then
2084 print("not joining trees"+DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr)+"\n");
2085 end if;
2086 elseif listEmpty(middle) then
2087 // We have the add and delete next to each other.
2088 // This means we can keep the diff as it since nothing moved.
2089 if debug then
2090 print("middle empty\n");
2091 end if;
2092 238 res := (Diff.Equal, before) :: listAppend(res, {(Diff.Equal, after)});
2093 else
2094 // We have a move with changes. Make the deleted simply be deleted.
2095 // The added parts become the Equal and Added parts in the diff
2096 ✗ res := list(i for i guard match i case (Diff.Delete,_) then false; else true; end match in res);
2097 ✗ if addedBeforeDeleted then
2098 ✗ res := (Diff.Equal, before) ::
2099 listAppend(res,
2100 (Diff.Equal, middle) ::
2101 (Diff.Delete, {deletedTree}) ::
2102 {(Diff.Equal, after)});
2103 else
2104 ✗ res := (Diff.Equal, before) ::
2105 (Diff.Delete, {deletedTree}) ::
2106 (Diff.Equal, middle) ::
2107 listAppend(res,{(Diff.Equal, after)});
2108 end if;
2109 end if;
2110 if debug then
2111 print(String(depth) + " merged tree size: " + String(stringLength(DiffAlgorithm.printActual(res, SimpleModelicaParser.parseTreeNodeStr))) + "\n");
2112 print(String(depth) + " before top="+firstTokenDebugStr(before)+"\n");
2113 print(" before all="+parseTreeStr(before)+"\n");
2114 print(" middle all="+parseTreeStr(middle)+"\n");
2115 print(" after all="+parseTreeStr(after)+"\n");
2116 print("middle top="+firstTokenDebugStr(middle)+"\n");
2117 print("after top="+firstTokenDebugStr(after)+"\n");
2118 print("added top="+firstTokenDebugStr(addedTree::{})+"\n");
2119 print("deleted top="+firstTokenDebugStr(deletedTree::{})+"\n");
2120 end if;
2121 elseif nadd>1 and ndel>1 then
2122 9 (addedTrees, deletedTrees) := extractAdditionsDeletions(res);
2123 // TODO: Move this into extractAdditionsDeletions?
2124
6/6
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 26 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 9 times.
35 addedTrees := list(t for t guard isLabeledNode(t) in addedTrees);
2125
6/6
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 19 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 13 times.
✓ Branch 6 taken 9 times.
28 deletedTrees := list(t for t guard isLabeledNode(t) in deletedTrees);
2126 if debug then
2127 print("number of labeled nodes. add="+String(listLength(addedTrees))+" del="+String(listLength(deletedTrees))+"\n");
2128 print(DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr) + "\n");
2129 end if;
2130 // We need to know if the labels changed order; if they didn't, we can improve the diff
2131
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 9 times.
50 for x in res loop
2132 41 (d,ts) := x;
2133
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 10 times.
41 if d==Diff.Equal then
2134 10 continue;
2135 end if;
2136 addList := {};
2137 delList := {};
2138
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 31 times.
76 for t in ts loop
2139
5/6
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 7 times.
✓ Branch 8 taken 14 times.
✓ Branch 9 taken 24 times.
45 if isEmpty(t) or parseTreeIsWhitespace(t) or isEmpty(nodeLabel(t))then
2140 21 continue;
2141 end if;
2142 48 str := parseTreeStr(nodeLabel(t)::{});
2143
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 13 times.
24 if d==Diff.Add then
2144 addList := str::addList;
2145 else
2146 delList := str::delList;
2147 end if;
2148 end for;
2149 end for;
2150 // O(D*D)
2151
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 9 times.
20 for added in addedTrees loop
2152 tryFind := false;
2153 try
2154 11 (deleted, deletedTrees) := List.findAndRemove1(deletedTrees, function compareNodeLabels(compare=compare), added);
2155 else
2156 try
2157 1 (deleted, deletedTrees) := List.findAndRemove1(deletedTrees, function compareNodeLabelsSpecial(compare=compare,delList=delList), added);
2158 else
2159 tryFind := true;
2160 end try;
2161 end try;
2162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if tryFind then
2163 ✗ continue;
2164 end if;
2165 11 resLocal := treeDiffWork(getNodes(deleted), getNodes(added), depth+1, compare);
2166 if debug then
2167 debugString1:=DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr);
2168 end if;
2169 11 res := replaceLabeledDiff(res, resLocal, nodeLabel(added), nodeLabel(deleted), compare, labelOrderDidNotChange(addList,delList));
2170 if debug then
2171 debugString2:=DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr);
2172 print("replaceLabeledDiff change for label:" + parseTreeNodeStr(nodeLabel(added)) + "\n");
2173 print("before replaceLabeledDiff: " + debugString1 + "\n");
2174 print("after replaceLabeledDiff: " + debugString2 + "\n");
2175 end if;
2176 end for;
2177 else
2178 // print(DiffAlgorithm.printDiffXml(res, parseTreeNodeStr) + "\n");
2179 end if;
2180 if debug then
2181 print("Before filter WS\n");
2182 print(DiffAlgorithm.printDiffXml(res, parseTreeNodeStr) + "\n");
2183 end if;
2184 151 res := filterDiffWhitespace(res);
2185 if debug then
2186 print("After filter WS\n");
2187 print(DiffAlgorithm.printDiffXml(res, parseTreeNodeStr) + "\n");
2188 end if;
2189 if depth==1 then
2190 // print(DiffAlgorithm.printDiffTerminalColor(res, parseTreeNodeStr) + "\n");
2191 end if;
2192 end treeDiffWork;
2193
2194 function compareNodeLabels
2195 input ParseTree t1, t2;
2196 input CmpParseTreeFunc compare;
2197 output Boolean b;
2198 algorithm
2199
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 b := compare(nodeLabel(t1),nodeLabel(t2));
2200 end compareNodeLabels;
2201
2202 function compareNodeLabelsSpecial
2203 input ParseTree t1, t2;
2204 input CmpParseTreeFunc compare;
2205 input list<String> delList;
2206 output Boolean b;
2207 algorithm
2208
3/6
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 time.
2 b := nodeLabelIsComponent(t1) and nodeLabelIsComponent(t2) and not listMember(parseTreeStr(nodeLabel(t1)::{}), delList);
2209 end compareNodeLabelsSpecial;
2210
2211 function nodeLabelIsComponent
2212 input ParseTree t1;
2213 output Boolean b;
2214 protected
2215 String contents;
2216 algorithm
2217 b := match nodeLabel(t1)
2218 case LEAF(token=Token.TOKEN(id=TokenId.IDENT, fileContents=contents))
2219 2 then 0==System.strncmp(contents, "$component:", 11);
2220 else false;
2221 end match;
2222 end nodeLabelIsComponent;
2223
2224 function filterDiffWhitespace
2225 input list<tuple<Diff,list<ParseTree>>> inDiff;
2226 output list<tuple<Diff,list<ParseTree>>> diff;
2227 protected
2228 list<tuple<Diff,list<ParseTree>>> diffLocal=inDiff;
2229 tuple<Diff,list<ParseTree>> diff1, diff2, diff3, diff4;
2230 Boolean firstIter, lastTokenNewline, hasAddedWS;
2231 list<ParseTree> tree, treeLocal, tree1, tree2, tree3, tree4, treeLast;
2232 ParseTree tree4First,t1,t2,t3,firstTreeSecondLast,firstTreeLast;
2233 Integer length, level;
2234 list<Integer> indentation;
2235 Diff diffEnum, diffEnum1, diffEnum2;
2236 String indentationStr;
2237 Token tok;
2238 algorithm
2239 diff := {};
2240 firstIter := true;
2241
2/2
✓ Branch 0 taken 1358 times.
✓ Branch 1 taken 302 times.
1660 while not listEmpty(diffLocal) loop
2242 // (diffEnum,tree) := listHead(diffLocal);
2243 // print(String(diffEnum) + ":\n");
2244 // print(parseTreeStr(tree));
2245 // print("\n");
2246 1358 (diffEnum, treeLast) := listHead(diffLocal);
2247 (firstTreeSecondLast, firstTreeLast) := match treeLast
2248 case {} then (EMPTY(),EMPTY());
2249 case {firstTreeLast} then (EMPTY(),firstTreeLast);
2250 else
2251 algorithm
2252
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 613 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 613 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 613 times.
613 {firstTreeSecondLast,firstTreeLast} := List.lastN(treeLast,2);
2253 then (firstTreeSecondLast,firstTreeLast);
2254 end match;
2255 diffLocal := match diffLocal
2256 // Empty node
2257 case (_, {})::diffLocal then diffLocal;
2258 // Do not delete whitespace in-between two tokens
2259 case (Diff.Delete, tree)::(diffLocal as ((Diff.Equal,_)::_))
2260 guard if firstIter then min(parseTreeIsWhitespaceNotComment(t) for t in tree) else false
2261 algorithm
2262 ✗ diff := (Diff.Equal, tree)::diff;
2263 then diffLocal;
2264 case (diff1 as (Diff.Equal,_))::(Diff.Delete, tree)::(diffLocal as ((Diff.Equal,_)::_))
2265 guard min(parseTreeIsWhitespaceNotComment(t) for t in tree)
2266 algorithm
2267 6 diff := (Diff.Equal, tree)::diff1::diff;
2268 then diffLocal;
2269 case (diff1 as (Diff.Equal,_))::(Diff.Delete, tree)::{}
2270 guard min(parseTreeIsWhitespaceNotComment(t) for t in tree)
2271 algorithm
2272 ✗ diff := (Diff.Equal, tree)::diff1::diff;
2273 then {};
2274 // Remove empty sections; they mess with rules further down
2275 case (_, tree)::diffLocal
2276 guard min(isEmpty(t) for t in tree)
2277 then diffLocal;
2278 case diff1::(_, tree)::diffLocal
2279 guard min(isEmpty(t) for t in tree)
2280 then diff1::diffLocal;
2281 case (diffEnum, tree)::diffLocal
2282 guard max(isEmpty(t) for t in tree)
2283 ✗ then (diffEnum,list(t for t guard not isEmpty(t) in tree))::diffLocal;
2284 case diff1::(diffEnum, tree)::diffLocal
2285 guard max(isEmpty(t) for t in tree)
2286 ✗ then diff1::(diffEnum,list(t for t guard not isEmpty(t) in tree))::diffLocal;
2287 // Sometimes a comment may move between 2 trees. This can bring it back, keeping the diff smaller.
2288 case (Diff.Delete,tree1)::(diff2 as (_, tree2))::(diff3 as (_, tree3))::(Diff.Add, tree4First::tree4)::diffLocal
2289 guard min(parseTreeIsWhitespaceNotComment(t) for t in tree2) and
2290 min(parseTreeIsWhitespaceNotComment(t) for t in tree3) and modelicaDiffTokenEq(lastToken(firstTreeLast),firstTokenInTree(tree4First))
2291 3 then (Diff.Delete,removeLastTokenInTrees(tree1))::(Diff.Equal,{LEAF(lastToken(firstTreeLast))})::(Diff.Add,removeFirstTokenInTree(tree4First)::tree4)::diff2::diff3::diffLocal;
2292 case (diff1 as (Diff.Equal,tree1 as (_::_)))::(Diff.Delete, tree)::(diffLocal as ((Diff.Equal,tree2 as (_::_))::_))
2293 guard needsWhitespaceBetweenTokens(lastToken(firstTreeLast), firstTokenInTree(listGet(tree2, 1)))
2294 algorithm
2295 2 diff := (Diff.Equal, {LEAF(makeToken(TokenId.WHITESPACE, " "))})::diff1::diff;
2296 then diffLocal;
2297 case (diff1 as (Diff.Equal,tree1 as (_::_)))::(diff2 as (Diff.Add, tree2 as (_::_)))::diffLocal
2298 guard needsWhitespaceBetweenTokens(lastToken(firstTreeLast), firstTokenInTree(listGet(tree2, 1)))
2299 algorithm
2300 2 diffLocal := diff1::(Diff.Equal, {LEAF(makeToken(TokenId.WHITESPACE, " "))})::diff2::diffLocal;
2301 then diffLocal;
2302 case (diff1 as (Diff.Equal,tree1 as (_::_)))::(Diff.Delete, tree)::(diffLocal as ((Diff.Equal,tree2 as (_::_))::_))
2303 algorithm
2304 diff := diff1::diff;
2305 9 then (Diff.Delete, tree)::diffLocal;
2306 // Do not add whitespace for no good reason. Do add whitespace.
2307 case (Diff.Add, tree)::(diffLocal as ((Diff.Equal,_)::_))
2308 guard if firstIter then min(parseTreeIsWhitespaceNotComment(t) for t in tree) else false
2309 then diffLocal;
2310 case (diff1 as (Diff.Equal,_))::(Diff.Add, tree)::(diffLocal as ((Diff.Equal,_)::_))
2311 guard min(parseTreeIsWhitespaceNotComment(t) for t in tree)
2312 algorithm
2313 diff := diff1::diff;
2314 then diffLocal;
2315 // DEL(..., NOT(NEWLINE)) ADD(..., NEWLINE) EQUAL(...) => DEL(..., NOT(NEWLINE)) ADD(...) EQUAL(...)
2316 case (diff1 as (Diff.Delete,tree1))::(Diff.Add, tree2)::diffLocal as ((Diff.Equal, tree3)::_)
2317 guard (not parseTreeIsNewLine(firstTreeLast)) and parseTreeIsNewLine(List.last(tree2))
2318 1 then diff1::(Diff.Add, List.stripLast(tree2))::diffLocal;
2319
2320 // DEL(IDENT) + ADD(WS, IDENT) => DEL(IDENT) + ADD(IDENT)
2321 case (diff1 as (Diff.Delete,{t1}))::(Diff.Add,{t2,t3})::diffLocal
2322 guard parseTreeIsOnlyIdent(t1) and parseTreeIsOnlyIdent(t3) and parseTreeIsWhitespaceNotComment(t2)
2323 3 then diff1::(Diff.Add,{t3})::diffLocal;
2324 // DEL(IDENT) + ADD(IDENT, WS) => DEL(IDENT) + ADD(IDENT)
2325 case (diff1 as (Diff.Delete,{t1}))::(Diff.Add,{t2,t3})::diffLocal
2326 guard parseTreeIsOnlyIdent(t1) and parseTreeIsOnlyIdent(t2) and parseTreeIsWhitespaceNotComment(t3)
2327 1 then diff1::(Diff.Add,{t2})::diffLocal;
2328
2329 // EQ(...) + ADD(..., NOT(NEWLINE)) + EQ(END, ...) => EQ(...) + ADD(..., NEWLINE) + EQ(END, ...)
2330 case (diff1 as (Diff.Equal,_))::(Diff.Add,tree2)::diffLocal as (Diff.Equal,t1::_)::_
2331 guard not parseTreeIsNewLine(List.last(tree2)) and parseTreeIsOnlyEnd(t1)
2332 ✗ then diff1::(Diff.Add,listAppend(tree2, {LEAF(makeToken(TokenId.NEWLINE, "\n"))}))::diffLocal;
2333
2334 // EQ(...) + ADD(WS, ...) => EQ(...) + ADD(...)
2335 case (diff1 as (Diff.Equal,tree1))::(Diff.Add, tree2 as _::_::_)::diffLocal
2336 guard not needsWhitespaceBetweenTokens(lastToken(firstTreeLast),firstTokenInTree(List.second(tree2))) and parseTreeIsWhitespaceNotComment(listHead(tree2)) and not parseTreeIsNewLine(firstTreeLast)
2337 ✗ then diff1::(Diff.Add, listRest(tree2))::diffLocal;
2338
2339 // ADD(..., WS) EQ(...) => ADD(...) + EQ(...)
2340 case (Diff.Add,tree1 as _::_::_)::(diff2 as (Diff.Equal, tree2))::diffLocal
2341 guard not needsWhitespaceBetweenTokens(lastToken(firstTreeLast),firstTokenInTree(listHead(tree2))) and not (parseTreeIsNewLine(firstTreeSecondLast) or parseTreeIsLineComment(firstTreeSecondLast)) and parseTreeIsWhitespaceNotCommentOrNewline(firstTreeLast)
2342 1 then (Diff.Add, List.stripLast(tree1))::diff2::diffLocal;
2343
2344 // EQ(NEWLINE) + ADD(NEWLINE, ...) => EQ(NEWLINE) + ADD(...)
2345 case (diff1 as (Diff.Equal,tree1))::(Diff.Add, tree2)::diffLocal
2346 guard parseTreeIsNewLine(firstTreeLast) and parseTreeIsNewLine(listHead(tree2))
2347 ✗ then diff1::(Diff.Add, listRest(tree2))::diffLocal;
2348 // NEWLINE ADD WS DEL => NEWLINE WS ADD DEL
2349 case (diff1 as (Diff.Equal,tree1))::(diff2 as (Diff.Add, tree2))::(diff3 as (Diff.Equal, tree3))::(diff4 as (Diff.Delete, tree4First::tree4))::diffLocal
2350 guard parseTreeIsNewLine(firstTreeLast) and min(parseTreeIsWhitespaceNotComment(t) for t in tree3)
2351 then diff1::diff3::diff2::diff4::diffLocal;
2352 // NEWLINE DEL WS ADD => NEWLINE WS DEL ADD
2353 case (diff1 as (Diff.Equal,tree1))::(diff2 as (Diff.Add, tree2))::(diff3 as (Diff.Equal, tree3))::(diff4 as (Diff.Delete, tree4First::tree4))::diffLocal
2354 guard parseTreeIsNewLine(firstTreeLast) and min(parseTreeIsWhitespaceNotComment(t) for t in tree3)
2355 then diff1::diff3::diff2::diff4::diffLocal;
2356
2357 case (diffEnum1,tree1)::(diffEnum2, tree2)::diffLocal
2358 guard diffEnum1==diffEnum2
2359 166 then (diffEnum1, listAppend(tree1, tree2))::diffLocal;
2360
2361 // ADD(WS) NEWLINE => NEWLINE
2362 case (Diff.Add,tree1)::(diffLocal as ((Diff.Equal,tree2)::_))
2363 guard tokenId(lastToken(firstTreeLast))==TokenId.WHITESPACE and tokenId(firstToken(tree2))==TokenId.NEWLINE
2364 algorithm
2365 1 diff := (Diff.Add,removeLastTokenInTrees(tree1))::diff;
2366 then diffLocal;
2367
2368 // A normal tree :)
2369 case diff1::diffLocal
2370 algorithm
2371 diff := diff1::diff;
2372 then diffLocal;
2373 end match;
2374 firstIter := false;
2375 end while;
2376 302 diff := listReverseInPlace(diff);
2377 // Look for indentation levels, try to fix added \n+WS to indent at the same level
2378 lastTokenNewline := false;
2379 indentation := {};
2380 hasAddedWS := false;
2381
2/2
✓ Branch 0 taken 1115 times.
✓ Branch 1 taken 302 times.
1417 for d in diff loop
2382 () := match d
2383 case (Diff.Add,tree)
2384 algorithm
2385
2/2
✓ Branch 1 taken 471 times.
✓ Branch 2 taken 362 times.
833 for t in tree loop
2386 () := match firstNTokensInTree_reverse(t, 2)
2387 case {LexerModelicaDiff.TOKEN(id=TokenId.WHITESPACE, length=length),LexerModelicaDiff.TOKEN(id=TokenId.NEWLINE)}
2388 algorithm
2389 hasAddedWS := true;
2390 then ();
2391 case {LexerModelicaDiff.TOKEN(id=TokenId.WHITESPACE, length=length)} guard lastTokenNewline
2392 algorithm
2393 hasAddedWS := true;
2394 then ();
2395 else ();
2396 end match;
2397 end for;
2398 then ();
2399 case (_,tree)
2400 algorithm
2401
2/2
✓ Branch 1 taken 2725 times.
✓ Branch 2 taken 753 times.
3478 for t in tree loop
2402 () := match firstNTokensInTree_reverse(t, 2)
2403 case {LexerModelicaDiff.TOKEN(id=TokenId.WHITESPACE, length=length),LexerModelicaDiff.TOKEN(id=TokenId.NEWLINE)}
2404 algorithm
2405 indentation := length::indentation;
2406 lastTokenNewline := false;
2407 then ();
2408 case {LexerModelicaDiff.TOKEN(id=TokenId.WHITESPACE, length=length)} guard lastTokenNewline
2409 algorithm
2410 indentation := length::indentation;
2411 lastTokenNewline := false;
2412 then ();
2413 case {LexerModelicaDiff.TOKEN(id=TokenId.NEWLINE)}
2414 algorithm
2415 lastTokenNewline := true;
2416 then ();
2417 case _
2418 algorithm
2419 lastTokenNewline := false;
2420 then ();
2421 end match;
2422 end for;
2423 then ();
2424 end match;
2425 end for;
2426
4/4
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 155 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 122 times.
302 if listEmpty(indentation) or (not hasAddedWS) then
2427 if debug then
2428 print("Skipping indentation as we could not auto-detect suitable indentation levels\n");
2429 end if;
2430 277 return;
2431 end if;
2432 // We have a known indentation level and added \n+WS; try to fix it
2433
4/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 25 times.
50 level := min(l for l in indentation);
2434 25 indentationStr := StringUtil.repeat(" ", level);
2435 diffLocal := {};
2436
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 25 times.
117 for d in diff loop
2437 () := match d
2438 case (Diff.Delete,tree)
2439 algorithm
2440 diffLocal := d::diffLocal;
2441 then ();
2442 case (diffEnum, tree)
2443 algorithm
2444 treeLocal := {};
2445 hasAddedWS := false;
2446
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 69 times.
213 for t in tree loop
2447 () := match (diffEnum, firstNTokensInTree_reverse(t, 2))
2448 case (Diff.Equal, _)
2449 algorithm
2450 then ();
2451 case (_, {LexerModelicaDiff.TOKEN(id=TokenId.WHITESPACE, length=length),tok as LexerModelicaDiff.TOKEN(id=TokenId.NEWLINE)})
2452 algorithm
2453 50 treeLocal := replaceFirstTokensInTree(t, {tok,makeToken(TokenId.WHITESPACE, indentationStr)})::treeLocal;
2454 hasAddedWS := true;
2455 then ();
2456 case (_, {LexerModelicaDiff.TOKEN(id=TokenId.WHITESPACE, length=length)}) guard lastTokenNewline
2457 algorithm
2458 ✗ treeLocal := replaceFirstTokensInTree(t, {makeToken(TokenId.WHITESPACE, indentationStr)})::treeLocal;
2459 hasAddedWS := true;
2460 then ();
2461 else
2462 algorithm
2463 treeLocal := t::treeLocal;
2464 then ();
2465 end match;
2466 lastTokenNewline := match lastToken(t) case LexerModelicaDiff.TOKEN(id=TokenId.NEWLINE) then true; else false; end match;
2467 end for;
2468
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 44 times.
69 diffLocal := if hasAddedWS then ((diffEnum, listReverse(treeLocal))::diffLocal) else (d::diffLocal);
2469 then ();
2470 end match;
2471 end for;
2472 25 diff := listReverseInPlace(diffLocal);
2473 end filterDiffWhitespace;
2474
2475 function labelOrderDidNotChange
2476 input list<String> addList, delList;
2477 output Boolean b;
2478 protected
2479 list<String> acc = {}, del = delList;
2480 String s;
2481 algorithm
2482 b := false;
2483
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 11 times.
24 for item in addList loop
2484
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if listMember(item, acc) then
2485 ✗ return;
2486 end if;
2487
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if listMember(item, del) then
2488 ✗ while item <> listHead(del) loop
2489 ✗ s::del := del;
2490 ✗ if listMember(s, acc) then
2491 ✗ return;
2492 end if;
2493 acc := s::acc;
2494 end while;
2495 ✗ del := listRest(del);
2496 end if;
2497 acc := item::acc;
2498 end for;
2499
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 for item in delList loop
2500 ✗ if listMember(item, acc) then
2501 ✗ return;
2502 end if;
2503 acc := item::acc;
2504 end for;
2505 b := true;
2506 end labelOrderDidNotChange;
2507
2508 function makeToken
2509 input TokenId id;
2510 input String str;
2511 output Token token;
2512 algorithm
2513 ✗ token := LexerModelicaDiff.TOKEN("<dummy>", id, str, 1, stringLength(str), 0, 0, 0, 0);
2514 annotation(__OpenModelica_EarlyInline=true);
2515 end makeToken;
2516
2517 function replaceLabeledDiff
2518 input list<tuple<Diff,list<ParseTree>>> inDiff, diffedNodes;
2519 input ParseTree labelOfDiffedAddedNodes, labelOfDiffedDeletedNodes;
2520 input CmpParseTreeFunc compare;
2521 input Boolean inAllLabelsAreInOrder "If all labels are in order, there are no moves and we can replace the deleted node which keeps whitespace in better positions";
2522 output list<tuple<Diff,list<ParseTree>>> res={};
2523 protected
2524 list<tuple<Diff,list<ParseTree>>> filtered;
2525 list<ParseTree> lst, acc;
2526 Boolean found=false, allLabelsAreInOrder=inAllLabelsAreInOrder;
2527 Diff d;
2528 algorithm
2529
3/4
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 10 times.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
11 if parseTreeStr(labelOfDiffedDeletedNodes::{}) == "$equation_section" then
2530 allLabelsAreInOrder := false;
2531 end if;
2532
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 11 times.
75 for diff in inDiff loop
2533 res := match diff
2534 case (Diff.Equal, _) then diff::res;
2535 case (Diff.Add, lst) guard not max(compare(nodeLabel(t), labelOfDiffedAddedNodes) for t in lst) then diff::res;
2536 case (Diff.Delete, lst) guard not max(compare(nodeLabel(t), labelOfDiffedDeletedNodes) for t in lst) then diff::res;
2537
7/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 15 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 10 times.
25 case (Diff.Add, lst) guard allLabelsAreInOrder then (Diff.Add, list(t for t guard not compare(nodeLabel(t), labelOfDiffedAddedNodes) in lst))::res; // TODO: Handle the deletion better...
2538
5/8
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 1 time.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 time.
2 case (Diff.Delete, lst) guard not allLabelsAreInOrder then (Diff.Delete, list(t for t guard not compare(nodeLabel(t), labelOfDiffedDeletedNodes) in lst))::res; // TODO: Handle the deletion better...
2539 case (d, lst)
2540 algorithm
2541 acc := {};
2542
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 12 times.
27 for t in lst loop
2543 // Assuming adjacent to the delete node
2544
7/12
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 10 times.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 1 time.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
17 if (not found) and compare(nodeLabel(t), if allLabelsAreInOrder then labelOfDiffedDeletedNodes else labelOfDiffedAddedNodes) then
2545 if not listEmpty(acc) then
2546 res := (Diff.Add, listReverse(acc))::res;
2547 acc := {};
2548 end if;
2549 // filtered := listReverse(diffedNodes);
2550
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 11 times.
52 filtered := listReverse(i for i guard match i case (Diff.Delete,_) then false; else true; end match in diffedNodes);
2551 11 res := listAppend(filtered, res);
2552 11 found := true;
2553 else
2554 4 res := (d, {t})::res;
2555 end if;
2556 end for;
2557 if not listEmpty(acc) then
2558 res := (Diff.Add, listReverse(acc))::res;
2559 end if;
2560 then res;
2561 end match;
2562 end for;
2563 11 res := listReverse(res);
2564 end replaceLabeledDiff;
2565
2566 function isEmpty
2567 input ParseTree tree;
2568 output Boolean b;
2569 algorithm
2570 b := match tree
2571 case EMPTY() then true;
2572 else false;
2573 end match;
2574 end isEmpty;
2575
2576 function isLabeledNode
2577 input ParseTree tree;
2578 output Boolean b;
2579 algorithm
2580 b := match tree
2581 case NODE(label=EMPTY()) then false;
2582 case NODE() then true;
2583 else false;
2584 end match;
2585 end isLabeledNode;
2586
2587 function nodeLabel
2588 input ParseTree tree;
2589 output ParseTree label;
2590 algorithm
2591 label := match tree
2592 162 case NODE() then tree.label;
2593 else EMPTY();
2594 end match;
2595 end nodeLabel;
2596
2597 function parseTreeEq
2598 input ParseTree t1, t2;
2599 input array<Token> diffSubtreeWorkArray1, diffSubtreeWorkArray2;
2600 output Boolean b;
2601 protected
2602 Integer len1, len2, commentLen1, commentLen2;
2603 algorithm
2604 // try
2605 2486 (len1,commentLen1) := findTokens(t1, diffSubtreeWorkArray1);
2606 2486 (len2,commentLen2) := findTokens(t2, diffSubtreeWorkArray2);
2607 /*else
2608 print("parseTreeEq failed: t1=" + parseTreeStr({t1}) + "\n");
2609 print("parseTreeEq failed: t2=" + parseTreeStr({t2}) + "\n");
2610 end try;*/
2611 b := false;
2612
4/4
✓ Branch 0 taken 1553 times.
✓ Branch 1 taken 933 times.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 1529 times.
2486 if len1 <> len2 or commentLen1 <> commentLen2 then
2613 957 return;
2614 end if;
2615
2/2
✓ Branch 0 taken 1290 times.
✓ Branch 1 taken 239 times.
9058 for i in 1:len1 loop
2616
2/2
✓ Branch 3 taken 788 times.
✓ Branch 4 taken 7529 times.
8317 if not modelicaDiffTokenEq(diffSubtreeWorkArray1[i], diffSubtreeWorkArray2[i]) then
2617 788 return;
2618 end if;
2619 end for;
2620
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 720 times.
762 for i in 1:commentLen1 loop
2621
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
42 if not modelicaDiffTokenEq(diffSubtreeWorkArray1[arrayLength(diffSubtreeWorkArray1)-(i-1)], diffSubtreeWorkArray2[arrayLength(diffSubtreeWorkArray2)-(i-1)]) then
2622 ✗ return;
2623 end if;
2624 end for;
2625 b := true;
2626 end parseTreeEq;
2627
2628 function findTokens
2629 input ParseTree t;
2630 input array<Token> work;
2631 input Integer inCount=0;
2632 input Integer inCommentCount=0;
2633 output Integer count=inCount;
2634 output Integer commentCount=inCommentCount;
2635 algorithm
2636
2/2
✓ Branch 1 taken 145 times.
✓ Branch 2 taken 142186 times.
142331 if parseTreeIsComment(t) then
2637 290 arrayUpdate(work, arrayLength(work)-commentCount, firstTokenInTree(t));
2638 145 commentCount := commentCount + 1;
2639 145 return;
2640 elseif parseTreeIsWhitespace(t) then
2641 27329 return;
2642 end if;
2643 () := match t
2644 case EMPTY() then ();
2645 case LEAF()
2646 algorithm
2647 69063 count := count+1;
2648 69063 arrayUpdate(work, count, t.token);
2649 then ();
2650 case NODE()
2651 algorithm
2652
2/2
✓ Branch 0 taken 137359 times.
✓ Branch 1 taken 45771 times.
183130 for n in t.nodes loop
2653 137359 (count, commentCount) := findTokens(n, work, count, commentCount);
2654 end for;
2655 then ();
2656 end match;
2657 end findTokens;
2658
2659 function replaceFirstTokensInTree
2660 input ParseTree t;
2661 input list<Token> tokens;
2662 output ParseTree tree;
2663 algorithm
2664
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
25 (tree, {}) := replaceFirstTokensInTreeWork(t, tokens);
2665 end replaceFirstTokensInTree;
2666
2667 function replaceFirstTokensInTreeWork
2668 input ParseTree t;
2669 input list<Token> inTokens;
2670 output ParseTree tree=t;
2671 output list<Token> tokens=inTokens;
2672 protected
2673 list<ParseTree> work, acc;
2674 ParseTree n;
2675 Token tok;
2676 algorithm
2677 (tree, tokens) := match (tree, tokens)
2678 case (tree, {}) then (tree, tokens);
2679 case (EMPTY(), _) then (tree, tokens);
2680 50 case (LEAF(), tok::tokens) then (LEAF(tok), tokens);
2681 case (NODE(), tokens)
2682 algorithm
2683 32 work := tree.nodes;
2684 acc := {};
2685
1/2
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
57 while not listEmpty(work) loop
2686 57 n::work := work;
2687 57 (n, tokens) := replaceFirstTokensInTreeWork(n, tokens);
2688
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 25 times.
57 if listEmpty(tokens) then
2689 32 tree.nodes := List.append_reverse(acc, n::work);
2690 32 return;
2691 else
2692 acc := n::acc;
2693 end if;
2694 end while;
2695 ✗ tree.nodes := listReverse(acc);
2696 ✗ then (tree, tokens);
2697 end match;
2698 end replaceFirstTokensInTreeWork;
2699
2700 function firstNTokensInTree_reverse
2701 input ParseTree t;
2702 input Integer n;
2703 input list<Token> acc={};
2704 output list<Token> tokens=acc;
2705 algorithm
2706
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5397 times.
5397 if listLength(tokens)>1 then
2707 ✗ return;
2708 end if;
2709 tokens := match t
2710 case EMPTY() then tokens;
2711 4062 case LEAF() then t.token::tokens;
2712 case NODE()
2713 algorithm
2714
2/2
✓ Branch 0 taken 2057 times.
✓ Branch 1 taken 56 times.
2113 for node in t.nodes loop
2715 2057 tokens := firstNTokensInTree_reverse(node, n, tokens);
2716
2/2
✓ Branch 1 taken 1279 times.
✓ Branch 2 taken 778 times.
2057 if listLength(tokens)>1 then
2717 1279 return;
2718 end if;
2719 end for;
2720 then acc;
2721 end match;
2722 end firstNTokensInTree_reverse;
2723
2724 function removeFirstTokenInTree
2725 input output ParseTree t;
2726 algorithm
2727 t := match t
2728 local
2729 ParseTree node, label;
2730 list<ParseTree> nodes;
2731 ✗ case EMPTY() then fail();
2732 case LEAF() then EMPTY();
2733 ✗ case NODE(label=label, nodes=node::nodes) then makeNode(removeFirstTokenInTree(node) :: nodes, label=label);
2734 end match;
2735 end removeFirstTokenInTree;
2736
2737 function removeLastTokenInTree
2738 input output ParseTree t;
2739 algorithm
2740 t := match t
2741 local
2742 ParseTree node, label;
2743 list<ParseTree> nodes;
2744 ✗ case EMPTY() then fail();
2745 case LEAF() then EMPTY();
2746 case NODE(label=label, nodes=nodes)
2747 algorithm
2748
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 node::nodes := listReverse(nodes);
2749 4 then makeNode(listReverse(removeLastTokenInTree(node) :: nodes), label=label);
2750 end match;
2751 end removeLastTokenInTree;
2752
2753 function removeLastTokenInTrees
2754 input output list<ParseTree> ts;
2755 protected
2756 ParseTree t;
2757 algorithm
2758
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 t :: ts := listReverse(ts);
2759 4 ts := listReverse(removeLastTokenInTree(t)::ts);
2760 end removeLastTokenInTrees;
2761
2762 function firstTokenInTree
2763 input ParseTree t;
2764 output Token token;
2765 algorithm
2766 token := match t
2767 ✗ case EMPTY() algorithm print("No first token in tree\n"); then fail();
2768 370 case LEAF() then t.token;
2769 140 case NODE() then firstTokenInTree(listGet(t.nodes, 1));
2770 end match;
2771 end firstTokenInTree;
2772
2773 function lastToken
2774 input ParseTree t;
2775 output Token token;
2776 algorithm
2777 token := match t
2778 ✗ case EMPTY() algorithm if debug then print("lastToken fail\n"); end if; then fail();
2779 638 case LEAF() then t.token;
2780 358 case NODE() then lastToken(List.last(t.nodes));
2781 end match;
2782 end lastToken;
2783
2784 function fixMoveOperations "Move operations are very common, but result
2785 in delete+add operations in the diff algorithm. Here we fix things so
2786 the addition is using the exact same tokens as the original.
2787
2788 Time cost O(N*D). The number of diffs is typically low since we compare
2789 parse trees"
2790 input list<tuple<Diff,list<ParseTree>>> inDiff;
2791 input CmpParseTreeFunc compare;
2792 output list<tuple<Diff,list<ParseTree>>> diff = {};
2793 protected
2794 list<ParseTree> lst, deleted={}, lst2;
2795 Boolean changeFound=false;
2796 tuple<Diff, list<ParseTree>> d1;
2797 algorithm
2798
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 12 times.
62 for d in inDiff loop
2799 () := match d
2800 case (Diff.Delete, lst)
2801 algorithm
2802 17 deleted := listAppend(lst, deleted);
2803 then ();
2804 else ();
2805 end match;
2806 end for;
2807
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 11 times.
12 if listEmpty(deleted) then
2808 diff := inDiff;
2809 1 return;
2810 end if;
2811
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 11 times.
59 for d in inDiff loop
2812 d1 := match d
2813 case (Diff.Add, lst)
2814 algorithm
2815 d1 := d;
2816
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 17 times.
48 for l1 in lst loop
2817
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 30 times.
31 if List.isMemberOnTrue(l1, deleted, compare) then
2818 changeFound := true;
2819 lst2 := {};
2820
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for l2 in lst loop
2821 try
2822 2 lst2 := List.getMemberOnTrue(l2, deleted, compare)::lst2;
2823 else
2824 lst2 := l2::lst2;
2825 end try;
2826 end for;
2827 1 d1 := (Diff.Add, listReverseInPlace(lst2));
2828 break;
2829 end if;
2830 end for;
2831 then d1;
2832 else d;
2833 end match;
2834 diff := d1::diff;
2835 end for;
2836
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 10 times.
11 diff := if changeFound then listReverseInPlace(diff) else inDiff;
2837 end fixMoveOperations;
2838
2839 function makeNode
2840 input list<ParseTree> nodes;
2841 input ParseTree label = EMPTY();
2842 output ParseTree node;
2843 algorithm
2844 node := match (list(n for n guard not isEmpty(n) in nodes),label)
2845 case ({},EMPTY()) then EMPTY();
2846 case ({node},EMPTY()) then node;
2847 1699 else NODE(label, nodes);
2848 end match;
2849 end makeNode;
2850
2851 function makeNodePrependTree
2852 input list<ParseTree> nodes;
2853 input list<ParseTree> tree;
2854 input ParseTree label = EMPTY();
2855 output list<ParseTree> outTree;
2856 algorithm
2857
2/2
✓ Branch 0 taken 4540 times.
✓ Branch 1 taken 531 times.
5071 outTree := if not listEmpty(nodes) then makeNode(nodes, label)::tree else tree;
2858 end makeNodePrependTree;
2859
2860 function isLeaf
2861 input ParseTree t;
2862 output Boolean b;
2863 algorithm
2864 b := match t
2865 case LEAF() then true;
2866 else false;
2867 end match;
2868 end isLeaf;
2869
2870 function firstToken
2871 input list<ParseTree> t;
2872 output Token token;
2873 algorithm
2874 token := match t
2875 local
2876 list<ParseTree> nodes;
2877 14 case NODE(nodes=nodes)::_ then firstToken(nodes);
2878 case LEAF(token)::_ then token;
2879 else LexerModelicaDiff.noToken;
2880 end match;
2881 end firstToken;
2882
2883 function firstTokenDebugStr
2884 input list<ParseTree> t;
2885 output String str;
2886 protected
2887 list<Token> l;
2888 algorithm
2889 ✗ l := firstToken(t)::{};
2890 ✗ str := Error.infoStr(topTokenSourceInfo(l))+" "+topTokenStr(l);
2891 end firstTokenDebugStr;
2892
2893 function getNodes
2894 input ParseTree t;
2895 output list<ParseTree> nodes;
2896 algorithm
2897 nodes := match t
2898 257 case NODE() then t.nodes;
2899 else {t};
2900 end match;
2901 end getNodes;
2902
2903 function extractSingleAddDiffBeforeAndAfter "Ignores whitespace"
2904 input list<tuple<Diff,list<ParseTree>>> diffs;
2905 output ParseTree addedTree = EMPTY();
2906 output ParseTree deletedTree = EMPTY();
2907 output list<ParseTree> before = {}, middle = {}, after;
2908 output Boolean addedBeforeDeleted = false;
2909 protected
2910 Boolean foundAdded=false;
2911 Boolean foundDeleted=false;
2912 list<list<ParseTree>> acc={};
2913 list<ParseTree> trees, lst;
2914 Diff d;
2915 Integer addCount;
2916 algorithm
2917
2/2
✓ Branch 0 taken 438 times.
✓ Branch 1 taken 128 times.
566 for diff in diffs loop
2918 () := match diff
2919 case (Diff.Add, lst)
2920 algorithm
2921 addCount := 0;
2922
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 128 times.
258 for tree in lst loop
2923 130 addCount := addCount + 1;
2924
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
130 if parseTreeIsNewLine(tree) and addCount > 1 and addCount == listLength(lst) then
2925 ✗ acc := {tree}::acc;
2926 elseif parseTreeIsWhitespace(tree) then
2927 acc := acc;
2928 else
2929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if foundAdded then
2930 ✗ Error.addInternalError("Found multiple Add subtrees", sourceInfo());
2931 ✗ fail();
2932 end if;
2933 addedTree := tree;
2934 foundAdded := true;
2935
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 if foundDeleted then
2936 128 middle := List.flattenReverse(acc);
2937 else
2938 addedBeforeDeleted := true;
2939 ✗ before := List.flattenReverse(acc);
2940 end if;
2941 acc := {};
2942 end if;
2943 end for;
2944 then ();
2945 case (Diff.Delete, lst)
2946 algorithm
2947
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 128 times.
270 for tree in lst loop
2948
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 128 times.
142 if parseTreeIsWhitespace(tree) then
2949 acc := {tree}::acc;
2950 else
2951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if foundDeleted then
2952 ✗ Error.addInternalError("Found multiple Delete subtrees", sourceInfo());
2953 ✗ fail();
2954 end if;
2955 deletedTree := tree;
2956 foundDeleted := true;
2957
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if foundAdded then
2958 ✗ middle := List.flattenReverse(acc);
2959 else
2960 addedBeforeDeleted := false;
2961 128 before := List.flattenReverse(acc);
2962 end if;
2963 acc := {};
2964 end if;
2965 end for;
2966 then ();
2967 case (Diff.Equal, trees)
2968 algorithm
2969 acc := trees::acc;
2970 then ();
2971 case (d, _)
2972 algorithm
2973 ✗ Error.addInternalError("Found "+String(d)+" subtrees with multiple or zero entries", sourceInfo());
2974 ✗ then fail();
2975 end match;
2976 end for;
2977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 true := foundAdded;
2978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 true := foundDeleted;
2979 128 after := List.flattenReverse(acc);
2980 end extractSingleAddDiffBeforeAndAfter;
2981
2982 function extractAdditionsDeletions
2983 input list<tuple<Diff,list<ParseTree>>> diffs;
2984 output list<ParseTree> addedTrees, deletedTrees;
2985 protected
2986 list<list<ParseTree>> addedTreesAcc={}, deletedTreesAcc={};
2987 list<ParseTree> lst;
2988 algorithm
2989
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 9 times.
50 for diff in diffs loop
2990 () := match diff
2991 case (Diff.Add, lst)
2992 algorithm
2993 addedTreesAcc := lst::addedTreesAcc;
2994 then ();
2995 case (Diff.Delete, lst)
2996 algorithm
2997 deletedTreesAcc := lst::deletedTreesAcc;
2998 then ();
2999 else ();
3000 end match;
3001 end for;
3002 9 addedTrees := List.flattenReverse(addedTreesAcc);
3003 9 deletedTrees := List.flattenReverse(deletedTreesAcc);
3004 end extractAdditionsDeletions;
3005
3006 function countDiffAddDelete
3007 input list<tuple<Diff,list<ParseTree>>> diffs;
3008 output Integer nadd=0;
3009 output Integer ndel=0;
3010 protected
3011 Diff d;
3012 list<ParseTree> l;
3013 algorithm
3014
2/2
✓ Branch 0 taken 568 times.
✓ Branch 1 taken 163 times.
731 for diff in diffs loop
3015 568 (d,l) := diff;
3016
2/2
✓ Branch 0 taken 177 times.
✓ Branch 1 taken 391 times.
568 if d == Diff.Add then
3017
4/4
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 177 times.
✓ Branch 2 taken 216 times.
✓ Branch 3 taken 177 times.
393 nadd := nadd+sum(if parseTreeIsWhitespace(t) then 0 else 1 for t in l);
3018 elseif d == Diff.Delete then
3019
4/4
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 169 times.
✓ Branch 2 taken 197 times.
✓ Branch 3 taken 169 times.
366 ndel := ndel+sum(if parseTreeIsWhitespace(t) then 0 else 1 for t in l);
3020 end if;
3021 end for;
3022 end countDiffAddDelete;
3023
3024 constant list<TokenId> whiteSpaceTokenIds = {
3025 TokenId.LINE_COMMENT,
3026 TokenId.BLOCK_COMMENT,
3027 TokenId.NEWLINE,
3028 TokenId.WHITESPACE
3029 };
3030
3031 constant list<TokenId> whiteSpaceTokenIdsNotComment = {
3032 TokenId.NEWLINE,
3033 TokenId.WHITESPACE
3034 };
3035
3036 constant list<TokenId> tokenIdsComment = {
3037 TokenId.LINE_COMMENT,
3038 TokenId.BLOCK_COMMENT
3039 };
3040
3041 function parseTreeIsWhitespace
3042 input ParseTree t1;
3043 output Boolean b;
3044 protected
3045 TokenId id;
3046 algorithm
3047 b := match t1
3048 96658 case LEAF() then listMember(t1.token.id, whiteSpaceTokenIds);
3049 else false;
3050 end match;
3051 end parseTreeIsWhitespace;
3052
3053 function parseTreeIsNewLine
3054 input ParseTree t1;
3055 output Boolean b;
3056 protected
3057 TokenId id;
3058 algorithm
3059 b := match t1
3060 437 case LEAF() then t1.token.id == TokenId.NEWLINE;
3061 else false;
3062 end match;
3063 end parseTreeIsNewLine;
3064
3065 function parseTreeIsWhitespaceNotComment
3066 input ParseTree t1;
3067 output Boolean b;
3068 protected
3069 TokenId id;
3070 algorithm
3071 b := match t1
3072 201 case LEAF() then listMember(t1.token.id, whiteSpaceTokenIdsNotComment);
3073 else false;
3074 end match;
3075 end parseTreeIsWhitespaceNotComment;
3076
3077 function parseTreeIsWhitespaceNotCommentOrNewline
3078 input ParseTree t1;
3079 output Boolean b;
3080 protected
3081 TokenId id;
3082 algorithm
3083 b := match t1
3084 28 case LEAF() then t1.token.id == TokenId.WHITESPACE;
3085 else false;
3086 end match;
3087 end parseTreeIsWhitespaceNotCommentOrNewline;
3088
3089 function parseTreeIsComment
3090 input ParseTree t1;
3091 output Boolean b;
3092 protected
3093 TokenId id;
3094 algorithm
3095 b := match t1
3096 96537 case LEAF() then listMember(t1.token.id, tokenIdsComment);
3097 else false;
3098 end match;
3099 end parseTreeIsComment;
3100
3101 function parseTreeIsLineComment
3102 input ParseTree t1;
3103 output Boolean b;
3104 protected
3105 TokenId id;
3106 algorithm
3107 b := match t1
3108 32 case LEAF() then t1.token.id == TokenId.LINE_COMMENT;
3109 else false;
3110 end match;
3111 end parseTreeIsLineComment;
3112
3113 function parseTreeIsOnlyIdent
3114 input ParseTree t1;
3115 output Boolean b;
3116 protected
3117 TokenId id;
3118 algorithm
3119 b := match t1
3120 10 case LEAF() then t1.token.id == TokenId.IDENT;
3121 else false;
3122 end match;
3123 end parseTreeIsOnlyIdent;
3124
3125 function parseTreeIsOnlyEnd
3126 input ParseTree t1;
3127 output Boolean b;
3128 protected
3129 TokenId id;
3130 algorithm
3131 b := match t1
3132 94 case LEAF() then t1.token.id == TokenId.END;
3133 else false;
3134 end match;
3135 end parseTreeIsOnlyEnd;
3136
3137 function parseTreeFilterWhitespace
3138 input output ParseTree t;
3139 protected
3140 TokenId id;
3141 Boolean changed;
3142 ParseTree n2;
3143 list<ParseTree> nodes;
3144 algorithm
3145 t := match t
3146 case LEAF() guard listMember(t.token.id, whiteSpaceTokenIds) then EMPTY();
3147 case NODE()
3148 algorithm
3149 changed := false;
3150 nodes := {};
3151
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 37 times.
125 for n in t.nodes loop
3152 88 n2 := parseTreeFilterWhitespace(n);
3153
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 37 times.
88 if not referenceEq(n, n2) then
3154 changed := true;
3155 end if;
3156
2/2
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 51 times.
88 if not isEmpty(n2) then
3157 nodes := n2::nodes;
3158 end if;
3159 end for;
3160
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 then if changed then NODE(t.label, listReverse(nodes)) else t;
3161 else t;
3162 end match;
3163 end parseTreeFilterWhitespace;
3164
3165 function eatWhitespace
3166 extends partialParser;
3167 protected
3168 TokenId id;
3169 Token t;
3170 algorithm
3171 tree := inTree;
3172 14531 while match tokens case LexerModelicaDiff.TOKEN(id=id)::_ then listMember(id, {TokenId.LINE_COMMENT, TokenId.BLOCK_COMMENT, TokenId.NEWLINE, TokenId.WHITESPACE}); else false; end match loop
3173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3813 times.
3813 t::tokens := tokens;
3174 3813 tree := LEAF(t)::tree;
3175 end while;
3176 outTree := tree;
3177 end eatWhitespace;
3178
3179 function scanOpt
3180 extends partialParser;
3181 input TokenId id;
3182 output Boolean found;
3183 protected
3184 TokenId id2;
3185 Token t;
3186 list<Token> tokens2;
3187 algorithm
3188 5550 (tokens, tree) := eatWhitespace(tokens, inTree);
3189 (tokens, tree, found) := match tokens
3190 1708 case (t as LexerModelicaDiff.TOKEN(id=id2))::tokens2 guard id==id2 then (tokens2, LEAF(t)::tree, true);
3191 3842 else (tokens, tree, false);
3192 end match;
3193
2/2
✓ Branch 0 taken 1708 times.
✓ Branch 1 taken 3842 times.
5550 if not found then
3194 // We want whitespace to be part of the next node; not added as a
3195 // separate node that gets eaten in the previous one.
3196 // This is bad for performance resons (we eat the same whitespace multiple
3197 // times. But we do not want to backpatch and guess indentation level.
3198 outTree := inTree;
3199 tokens := inTokens;
3200 else
3201 outTree := tree;
3202 end if;
3203 end scanOpt;
3204
3205 function scan
3206 extends partialParser;
3207 input TokenId id;
3208 protected
3209 Boolean found;
3210 algorithm
3211 1306 tree := inTree;
3212 1306 (tokens, tree, found) := scanOpt(tokens, tree, id);
3213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1306 times.
1306 if not found then
3214 ✗ error(tokens, tree, {id});
3215 end if;
3216
1/2
✓ Branch 0 taken 1306 times.
✗ Branch 1 not taken.
1306 outTree := tree;
3217 end scan;
3218
3219 function scanOneOf
3220 extends partialParser;
3221 input list<TokenId> ids;
3222 protected
3223 Boolean found;
3224 algorithm
3225 135 tree := inTree;
3226 135 (tokens, tree, found) := LA1(tokens, tree, ids, consume=true);
3227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
135 if not found then
3228 ✗ error(tokens, tree, ids);
3229 end if;
3230
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
135 outTree := tree;
3231 end scanOneOf;
3232
3233 function error
3234 input list<Token> tokens;
3235 input list<ParseTree> tree;
3236 input list<TokenId> expected;
3237 protected
3238 Integer i;
3239 String s;
3240 list<String> strs, res;
3241 SourceInfo info;
3242 algorithm
3243 ✗ info := topTokenSourceInfo(tokens);
3244 ✗ res := ("Failed to scan top of input: " + (if debug then debugTokenStr(tokens) else topTokenStr(tokens)) + "\n Expected one of: " + (if listEmpty(expected) then "<EOF>" else stringDelimitList(list(tokenIdStr(id) for id in expected), ", ")) + "\n")::{};
3245 ✗ res := (" Current parse tree is:\n" + parseTreeStr(listReverse(tree)) + "\n The parser stack is:\n")::res;
3246 ✗ StackOverflow.setStacktraceMessages(0, 100);
3247 ✗ for s in StackOverflow.readableStacktraceMessages() loop
3248 ✗ (i, strs) := System.regex(s, "SimpleModelicaParser[^A-Za-z]([A-Za-z_0-9_]*)", 2, true, false);
3249 () := match (i, strs)
3250 case (2, {_,s}) guard s<>"error"
3251 algorithm
3252 res := "\n"::s::res;
3253 then ();
3254 else ();
3255 end match;
3256 end for;
3257 ✗ Error.addInternalError(stringAppendList(listReverse(res)), info);
3258 ✗ fail();
3259 end error;
3260
3261 function tokenIdStr
3262 input TokenId id;
3263 output String str = String(id);
3264 end tokenIdStr;
3265
3266 function peek
3267 extends partialParser;
3268 output TokenId id;
3269 algorithm
3270 423 tree := inTree;
3271 423 (tokens, tree) := eatWhitespace(tokens, tree);
3272 id := match tokens
3273 case LexerModelicaDiff.TOKEN(id=id)::_ then id;
3274 else TokenId._NO_TOKEN;
3275 end match;
3276
1/2
✓ Branch 0 taken 423 times.
✗ Branch 1 not taken.
423 outTree := tree;
3277 end peek;
3278
3279 function consume
3280 extends partialParser;
3281 protected
3282 Token t;
3283 algorithm
3284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
414 t::tokens := tokens;
3285 414 outTree := LEAF(t)::inTree;
3286 end consume;
3287
3288 function LA1 "Do look-ahead 1 token and see if the token is one of the given ones."
3289 extends partialParser;
3290 input list<TokenId> ids;
3291 input Boolean consume=false;
3292 output Boolean found;
3293 protected
3294 TokenId id;
3295 algorithm
3296 4261 tree := inTree;
3297 4261 (tokens, tree) := eatWhitespace(tokens, tree);
3298 found := match tokens
3299 4169 case LexerModelicaDiff.TOKEN(id=id)::_ then listMember(id, ids);
3300 else false;
3301 end match;
3302
2/2
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 4056 times.
4261 if found and consume then
3303 205 (tokens,tree) := SimpleModelicaParser.consume(tokens, tree);
3304 end if;
3305
2/2
✓ Branch 0 taken 839 times.
✓ Branch 1 taken 3422 times.
4261 if not found then
3306 outTree := inTree;
3307 tokens := inTokens;
3308 else
3309 839 outTree := tree;
3310 end if;
3311 end LA1;
3312
3313 function LAk "Do look-ahead k tokens and see if the tokens match one of the given ones."
3314 extends partialParser;
3315 input list<list<TokenId>> idsLst "k sets of tokens to check";
3316 output Boolean found=true;
3317 protected
3318 TokenId id;
3319 list<Token> tmp;
3320 algorithm
3321 280 tree := inTree;
3322 280 (tokens, tree) := eatWhitespace(tokens, tree);
3323 280 outTree := tree;
3324 tmp := tokens;
3325
2/2
✓ Branch 0 taken 325 times.
✓ Branch 1 taken 27 times.
352 for ids in idsLst loop
3326 found := match tmp
3327 325 case LexerModelicaDiff.TOKEN(id=id)::tmp then listMember(id, ids);
3328 else false;
3329 end match;
3330
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 72 times.
325 if not found then
3331 253 return;
3332 end if;
3333 72 tmp := eatWhitespace(tmp, {});
3334 end for;
3335 end LAk;
3336
3337 function parseTreeStrWork
3338 input ParseTree tree;
3339 algorithm
3340 () := match tree
3341 // TODO: Normalize line-endings? We can output mixed CRLF/LF now...
3342 3907 case LEAF() algorithm Print.printBuf(tokenContent(tree.token)); then ();
3343 case EMPTY() then ();
3344 case NODE()
3345 algorithm
3346
2/2
✓ Branch 0 taken 4196 times.
✓ Branch 1 taken 1416 times.
5612 for n in tree.nodes loop
3347 4196 parseTreeStrWork(n);
3348 end for;
3349 then ();
3350 end match;
3351 end parseTreeStrWork;
3352
3353 function topTokenStr
3354 input list<Token> tokens;
3355 output String str;
3356 protected
3357 TokenId id;
3358 Token t;
3359 algorithm
3360 ✗ str := (match tokens case (t as LexerModelicaDiff.TOKEN(id=id))::_ then String(id)+" ("+tokenContent(t)+")"; else "EOF"; end match);
3361 end topTokenStr;
3362
3363 function debugTokenStr
3364 input list<Token> tokens;
3365 output String str;
3366 algorithm
3367 ✗ str := stringDelimitList(list(String(t.id)+" ("+tokenContent(t)+")" for t in tokens), "\n");
3368 end debugTokenStr;
3369
3370 function topTokenSourceInfo
3371 input list<Token> tokens;
3372 output SourceInfo info;
3373 protected
3374 Token t;
3375 algorithm
3376 info := (match tokens case t::_
3377 ✗ then LexerModelicaDiff.tokenSourceInfo(t);
3378 else SOURCEINFO("<SimpleModelicaParser>", false, 0, 0, 0, 0, 0.0); end match);
3379 end topTokenSourceInfo;
3380
3381 function needsWhitespaceBetweenTokens
3382 input Token first, last;
3383 output Boolean b;
3384 protected
3385 constant list<TokenId> notident = {
3386 TokenId.ASSIGN,
3387 TokenId.BLOCK_COMMENT,
3388 TokenId.COLON,
3389 TokenId.COLONCOLON,
3390 TokenId.COMMA,
3391 TokenId.DOT,
3392 TokenId.EQEQ,
3393 TokenId.EQUALS,
3394 TokenId.GREATER,
3395 TokenId.GREATEREQ,
3396 TokenId.LBRACE,
3397 TokenId.LBRACK,
3398 TokenId.LESS,
3399 TokenId.LESSEQ,
3400 TokenId.LESSGT,
3401 TokenId.LINE_COMMENT,
3402 TokenId.LPAR,
3403 TokenId.MINUS,
3404 TokenId.MINUS_EW,
3405 TokenId.NEWLINE,
3406 TokenId.OPERATOR,
3407 TokenId.PLUS,
3408 TokenId.PLUS_EW,
3409 TokenId.POWER,
3410 TokenId.POWER_EW,
3411 TokenId.RBRACE,
3412 TokenId.RBRACK,
3413 TokenId.RPAR,
3414 TokenId.SEMICOLON,
3415 TokenId.SLASH,
3416 TokenId.SLASH_EW,
3417 TokenId.STAR,
3418 TokenId.STAR_EW,
3419 // TokenId.STRING, Not OK for string comments
3420 TokenId.UNSIGNED_INTEGER,
3421 TokenId.UNSIGNED_REAL,
3422 TokenId.WHITESPACE
3423 };
3424 algorithm
3425
4/4
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 146 times.
✓ Branch 4 taken 76 times.
✓ Branch 5 taken 2 times.
302 if listMember(tokenId(first), notident) or listMember(tokenId(last), notident) then
3426 b := false;
3427 222 return;
3428 end if;
3429 // Assuming the grammar is nice to us...
3430 b := true;
3431 end needsWhitespaceBetweenTokens;
3432
3433 function tokenId
3434 input Token t;
3435 output TokenId id;
3436 algorithm
3437
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 1 time.
578 LexerModelicaDiff.TOKEN(id=id) := t;
3438 end tokenId;
3439
3440 package First "First token possible for a given non-terminal in the Modelica 3 grammar"
3441 constant list<TokenId> class_prefixes = {
3442 TokenId.PARTIAL,
3443 TokenId.CLASS,
3444 TokenId.MODEL,
3445 TokenId.OPERATOR,
3446 TokenId.RECORD,
3447 TokenId.BLOCK,
3448 TokenId.EXPANDABLE,
3449 TokenId.CONNECTOR,
3450 TokenId.TYPE,
3451 TokenId.PACKAGE,
3452 TokenId.PURE,
3453 TokenId.IMPURE,
3454 TokenId.FUNCTION
3455 };
3456 constant list<TokenId> class_definition =
3457 TokenId.FINAL ::
3458 TokenId.ENCAPSULATED ::
3459 class_prefixes
3460 ;
3461 constant list<TokenId> type_prefix = {
3462 TokenId.FLOW,
3463 TokenId.STREAM,
3464 TokenId.DISCRETE,
3465 TokenId.PARAMETER,
3466 TokenId.CONSTANT,
3467 TokenId.INPUT,
3468 TokenId.OUTPUT
3469 };
3470 constant list<TokenId> class_modification = {
3471 TokenId.LPAR
3472 };
3473 constant list<TokenId> _annotation = {
3474 TokenId.ANNOTATION
3475 };
3476 constant list<TokenId> element_redeclaration = {
3477 TokenId.REDECLARE
3478 };
3479 constant list<TokenId> name = {
3480 TokenId.DOT,
3481 TokenId.IDENT
3482 };
3483 constant list<TokenId> element_modification_or_replaceable =
3484 TokenId.EACH::
3485 TokenId.FINAL::
3486 TokenId.REPLACEABLE::
3487 name
3488 ;
3489 constant list<TokenId> argument = listAppend(
3490 element_modification_or_replaceable,
3491 element_redeclaration
3492 );
3493 constant list<TokenId> modification = {
3494 TokenId.LPAR,
3495 TokenId.EQUALS,
3496 TokenId.ASSIGN
3497 };
3498 constant list<TokenId> component_clause = listAppend(type_prefix,name);
3499 constant list<TokenId> element = listAppend(component_clause, listAppend(class_definition, {
3500 TokenId.ANNOTATION,
3501 TokenId.IMPORT,
3502 TokenId.EXTENDS,
3503 TokenId.REDECLARE,
3504 TokenId.FINAL,
3505 TokenId.INNER,
3506 TokenId.OUTER,
3507 TokenId.REPLACEABLE
3508 }));
3509 constant list<TokenId> statement = {
3510 TokenId.DOT,
3511 TokenId.IDENT,
3512 TokenId.LPAR,
3513 TokenId.BREAK,
3514 TokenId.RETURN,
3515 TokenId.IF,
3516 TokenId.FOR,
3517 TokenId.WHILE,
3518 TokenId.WHEN
3519 };
3520 constant list<TokenId> component_reference = {
3521 TokenId.DOT,
3522 TokenId.IDENT
3523 };
3524 /* constant list<TokenId> function_arguments =
3525 TokenId.FUNCTION ::
3526 TokenId.IDENT ::
3527 expression
3528 ; */
3529 end First;
3530
3531 package Follow
3532 constant list<TokenId> statement_equation = {
3533 TokenId.INITIAL, // Only potential conflict
3534 TokenId.EQUATION,
3535 TokenId.ALGORITHM,
3536 TokenId.PUBLIC,
3537 TokenId.PROTECTED,
3538 TokenId.EXTERNAL,
3539 TokenId.ANNOTATION,
3540 TokenId.ELSE,
3541 TokenId.ELSEIF,
3542 TokenId.END,
3543 TokenId.ELSEWHEN
3544 };
3545 end Follow;
3546
3547 constant Boolean debug = false;
3548
3549 annotation(__OpenModelica_Interface="backend_tools");
3550 end SimpleModelicaParser;
3551