Coverage for skema/program_analysis/CAST2FN/ann_cast/annotated_cast.py: 66%

540 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-30 17:15 +0000

1import typing 

2import difflib 

3 

4from skema.program_analysis.CAST2FN.model.cast import ( 

5 AstNode, 

6 Assignment, 

7 Attribute, 

8 Call, 

9 CASTLiteralValue, 

10 FunctionDef, 

11 Goto, 

12 Label, 

13 Loop, 

14 ModelBreak, 

15 ModelContinue, 

16 ModelIf, 

17 ModelReturn, 

18 Module, 

19 Name, 

20 Operator, 

21 RecordDef, 

22 Var, 

23) 

24 

25from skema.model_assembly.networks import GroundedFunctionNetwork, VariableNode 

26from skema.program_analysis.CAST2FN.model.cast.model_import import ( 

27 ModelImport, 

28) 

29 

30 

31class PipelineState: 

32 def __init__(self, ann_nodes: typing.List, grfn2_2: bool): 

33 self.GENERATE_GRFN_2_2 = grfn2_2 

34 self.PRINT_DEBUGGING_INFO = False 

35 self.nodes = ann_nodes 

36 # populated after IdCollapsePass, and used to give ids to GrFN condition variables 

37 self.collapsed_id_counter = 0 

38 # dict mapping FunctionDef container scopestr to its id 

39 self.func_con_scopestr_to_id = {} 

40 # dict mapping container scope strings to their nodes 

41 self.con_scopestr_to_node = {} 

42 # dict mapping function IDs to their FunctionDef nodes. 

43 self.func_id_to_def = {} 

44 self.grfn_id_to_grfn_var = {} 

45 # the fullid of a AnnCastName node is a string which includes its 

46 # variable name, numerical id, version, and scope 

47 self.fullid_to_grfn_id = {} 

48 # `module_node` is simply a reference to the AnnCastModule node 

49 # FUTURE: to handle multiple modules for Python this will need to be extended 

50 # for the most part, the module node is used to determine globals 

51 self.module_node = None 

52 # GrFN stored after ToGrfnPass 

53 self.grfn: typing.Optional[GroundedFunctionNetwork] = None 

54 

55 # flag deciding whether or not to use GE's interpretation of From Source 

56 # when populating metadata information 

57 # 

58 # For GE, all instances of a variable which exists in the source code should 

59 # be considered from source. 

60 # We think this loses information about the GrFN variables we create to facilitate 

61 # transition between interfaces. That is, we create many GrFN variables which 

62 # do not literally exist in source, and so don't consider those to be from source 

63 self.FROM_SOURCE_FOR_GE = True 

64 

65 self.gromet_collection = None 

66 

67 def get_nodes(self): 

68 return self.nodes 

69 

70 def is_var_local_to_func(self, scopestr: str, id: int): 

71 """ 

72 Precondition: scopestr is the scopestr of a FunctionDef 

73 Check if the variable with id `id` is neither a global nor a formal parameter 

74 """ 

75 if self.is_global_var(id): 

76 return False 

77 

78 func_def_node = self.func_def_node_from_scopestr(scopestr) 

79 for param in func_def_node.func_args: 

80 if id == param.val.id: 

81 return False 

82 

83 return True 

84 

85 def is_container(self, scopestr: str): 

86 """ 

87 Check if scopestr is a container scopestr 

88 """ 

89 return scopestr in self.con_scopestr_to_node 

90 

91 def con_node_from_scopestr(self, scopestr: str): 

92 """ 

93 Precondition: scopestr is a container scopestr 

94 Return the container node associated with scopestr 

95 """ 

96 return self.con_scopestr_to_node[scopestr] 

97 

98 def get_grfn(self) -> typing.Optional[GroundedFunctionNetwork]: 

99 return self.grfn 

100 

101 def func_def_exists(self, id: int) -> bool: 

102 """ 

103 Check if there is a FuncionDef for id 

104 """ 

105 return id in self.func_id_to_def 

106 

107 def is_con_scopestr_func_def(self, con_scopestr: str): 

108 return con_scopestr in self.func_con_scopestr_to_id 

109 

110 def func_def_node_from_scopestr(self, con_scopestr: str): 

111 """ 

112 Return the AnnCastFuncitonDef node for the container scope 

113 defined by `con_scopestr` 

114 """ 

115 function_id = self.func_con_scopestr_to_id[con_scopestr] 

116 return self.func_id_to_def[function_id] 

117 

118 def func_def_node_from_id(self, id: int): 

119 """ 

120 Return the FunctionDef for `id` 

121 """ 

122 assert self.func_def_exists(id) 

123 return self.func_id_to_def[id] 

124 

125 def is_global_var(self, id: int): 

126 """ 

127 Check if id is in the used_variables attribute of the module node 

128 """ 

129 return id in self.module_node.used_vars 

130 

131 def all_globals_dict(self): 

132 """ 

133 Return a dict mapping id to string name for all global variables 

134 """ 

135 return self.module_node.used_vars 

136 

137 def next_collapsed_id(self): 

138 """ 

139 Return the next collapsed id, and increment `collapsed_id_counter` 

140 """ 

141 to_return = self.collapsed_id_counter 

142 self.collapsed_id_counter += 1 

143 return to_return 

144 

145 def store_grfn_var(self, fullid: str, grfn_var: VariableNode): 

146 """ 

147 Cache `grfn_var` in `grfn_id_to_grfn_var` and add `fullid` to `fullid_to_grfn_id` 

148 """ 

149 self.fullid_to_grfn_id[fullid] = grfn_var.uid 

150 self.grfn_id_to_grfn_var[grfn_var.uid] = grfn_var 

151 

152 def grfn_var_exists(self, fullid: str): 

153 """ 

154 Returns the whether the GrFN VariableNode associated with `fullid` has already been created 

155 """ 

156 return fullid in self.fullid_to_grfn_id 

157 

158 def get_grfn_var(self, fullid: str): 

159 """ 

160 Returns the cached GrFN VariableNode associated with `fullid` 

161 """ 

162 grfn_id = self.fullid_to_grfn_id[fullid] 

163 return self.grfn_id_to_grfn_var[grfn_id] 

164 

165 def alias_grfn_vars(self, src_fullid: str, tgt_fullid: str): 

166 """ 

167 Put the GrFN id associated with `tgt_fullid` into dict `fullid_to_grfn_id` for key 

168 `src_fullid` 

169 """ 

170 self.fullid_to_grfn_id[src_fullid] = self.fullid_to_grfn_id[tgt_fullid] 

171 

172 def equiv(self, other): 

173 """ 

174 Check if the PipelineState nodes are equivalent to other's 

175 Used in the test suite 

176 """ 

177 # FUTURE: once the PiplelineState nodes attribute stores multiple modules, 

178 # we may need to check that the ordering is consistent. Currently, 

179 # CAST and the AnnnotatedCast nodes only have a single module, so this is not a concern 

180 for i, node in enumerate(self.nodes): 

181 if not node.equiv(other.nodes[i]): 

182 # printing diff to help locating difference 

183 # because we do not overwrite the __str__() methods, 

184 # this has limited usefullness, but its better than nothing 

185 print(f"AnnCast equiv failed:") 

186 self_lines = str(node).splitlines() 

187 other_lines = str(other.nodes[i]).splitlines() 

188 for i, diff in enumerate( 

189 difflib.ndiff(self_lines, other_lines) 

190 ): 

191 if diff[0] == " ": 

192 continue 

193 print(f"Line {i}: {diff}") 

194 

195 return False 

196 

197 return True 

198 

199 

200class AnnCastNode(AstNode): 

201 def __init__(self, *args, **kwargs): 

202 super().__init__(self) 

203 self.expr_str: str = "" 

204 

205 def to_dict(self): 

206 result = super().to_dict() 

207 result["expr_str"] = self.expr_str 

208 return result 

209 

210 def equiv(self, other): 

211 if not isinstance(other, AnnCastNode): 

212 return False 

213 return self.to_dict() == other.to_dict() 

214 

215 

216class AnnCastAssignment(AnnCastNode): 

217 def __init__(self, left, right, source_refs): 

218 super().__init__(self) 

219 self.left = left 

220 self.right = right 

221 self.source_refs = source_refs 

222 

223 self.grfn_assignment: GrfnAssignment 

224 

225 def to_dict(self): 

226 result = super().to_dict() 

227 result["left"] = self.left.to_dict() 

228 result["right"] = self.right.to_dict() 

229 return result 

230 

231 def equiv(self, other): 

232 if not isinstance(other, AnnCastAssignment): 

233 return False 

234 return self.to_dict() == other.to_dict() 

235 

236 def __str__(self): 

237 return Assignment.__str__(self) 

238 

239 

240class AnnCastAttribute(AnnCastNode): 

241 def __init__(self, value, attr, source_refs): 

242 super().__init__(self) 

243 self.value = value 

244 self.attr = attr 

245 self.source_refs = source_refs 

246 

247 def to_dict(self): 

248 result = super().to_dict() 

249 # FUTURE: add value and attr to result 

250 return result 

251 

252 def equiv(self, other): 

253 if not isinstance(other, AnnCastAttribute): 

254 return False 

255 return self.to_dict() == other.to_dict() 

256 

257 def __str__(self): 

258 return Attribute.__str__(self) 

259 

260 

261class AnnCastCall(AnnCastNode): 

262 def __init__(self, func, arguments, source_refs): 

263 super().__init__(self) 

264 self.func: AnnCastName = func 

265 self.arguments = arguments 

266 self.source_refs = source_refs 

267 

268 # the index of this Call node over all invocations of this function 

269 self.invocation_index: int 

270 

271 # dicts mapping a Name id to its fullid 

272 self.top_interface_in = {} 

273 self.top_interface_out = {} 

274 self.bot_interface_in = {} 

275 self.bot_interface_out = {} 

276 # dicts mapping Name id to Name string 

277 self.top_interface_vars = {} 

278 self.bot_interface_vars = {} 

279 # GrFN lambda expressions 

280 self.top_interface_lambda: str 

281 self.bot_interface_lambda: str 

282 

283 # for top_interface_out 

284 # mapping Name id to fullid 

285 # to determine this, we check if we store version 0 on any Name node 

286 self.globals_accessed_before_mod = {} 

287 self.used_globals = {} 

288 

289 # for bot_interface 

290 # map Name id to fullid 

291 self.in_ret_val = {} 

292 self.out_ret_val = {} 

293 

294 # if this is a GrFN 2.2 Call, we will copy the associated FunctionDef 

295 # to make the GrFN 2.2 container 

296 self.is_grfn_2_2: bool = False 

297 # copied function def for GrFN 2.2 

298 self.func_def_copy: typing.Optional[AnnCastFunctionDef] = None 

299 

300 # keep track of whether the Call has an associated FunctionDef 

301 # and if the Call should return a value 

302 self.has_func_def: bool = False 

303 self.has_ret_val: bool = False 

304 

305 # dict mapping argument index to created argument fullid 

306 self.arg_index_to_fullid = {} 

307 self.param_index_to_fullid = {} 

308 # this dict maps argument positional index to GrfnAssignment's 

309 # Each GrfnAssignment stores the ASSIGN/LITERAL node, 

310 # the inputs to the ASSIGN/LITERAL node, and the outputs to the ASSIGN/LITERAL node 

311 # In this case, the output will map the arguments fullid to its grfn_id 

312 self.arg_assignments: typing.Dict[int, GrfnAssignment] = {} 

313 

314 # metadata attributes 

315 self.grfn_con_src_ref: GrfnContainerSrcRef 

316 

317 def to_dict(self): 

318 result = super().to_dict() 

319 result["func"] = self.func.to_dict() 

320 result["arguments"] = [arg.to_dict() for arg in self.arguments] 

321 return result 

322 

323 def equiv(self, other): 

324 if not isinstance(other, AnnCastCall): 

325 return False 

326 return self.to_dict() == other.to_dict() 

327 

328 def __str__(self): 

329 return Call.__str__(self) 

330 

331class AnnCastFunctionDef(AnnCastNode): 

332 def __init__(self, name, func_args, body, source_refs): 

333 super().__init__(self) 

334 self.name = name 

335 self.func_args = func_args 

336 self.body = body 

337 self.source_refs = source_refs 

338 

339 self.con_scope: typing.List 

340 

341 # For CAST coming from C, we determine if the function 

342 # has a return value by looking for a CAST Return node. 

343 # We do this during the ContainerScopePass. 

344 # FUTURE: What should be done with CAST coming from Python? 

345 # In Python, every function returns something, 

346 # either None or the explicit return value 

347 self.has_ret_val: bool = False 

348 # for bot_interface 

349 # in_ret_val and out_ret_val map Name id to fullid 

350 self.in_ret_val = {} 

351 self.out_ret_val = {} 

352 

353 # dicts mapping a Name id to its string name 

354 # used for container interfaces 

355 self.modified_vars: typing.Dict[int, str] 

356 self.vars_accessed_before_mod: typing.Dict[int, str] 

357 self.used_vars: typing.Dict[int, str] 

358 # for now, top_interface_vars and bot_interface_vars only include globals 

359 # since those variables cross the container boundaries 

360 self.top_interface_vars: typing.Dict[int, str] = {} 

361 self.bot_interface_vars: typing.Dict[int, str] = {} 

362 # dicts for global variables 

363 # for top_interface_out 

364 # mapping Name id to fullid 

365 # to determine this, we check if we store version 0 on any Name node 

366 self.globals_accessed_before_mod = {} 

367 self.used_globals = {} 

368 # for bot interface in 

369 self.modified_globals = {} 

370 

371 # dict mapping argument index to created argument fullid 

372 self.arg_index_to_fullid = {} 

373 self.param_index_to_fullid = {} 

374 

375 # dicts mapping a Name id to its fullid 

376 self.top_interface_in = {} 

377 self.top_interface_out = {} 

378 self.bot_interface_in = {} 

379 self.bot_interface_out = {} 

380 # GrFN lambda expressions 

381 self.top_interface_lambda: str 

382 self.bot_interface_lambda: str 

383 

384 # dict mapping Name id to highest version at end of "block" 

385 self.body_highest_var_vers = {} 

386 

387 # metadata attributes 

388 self.grfn_con_src_ref: GrfnContainerSrcRef 

389 

390 # dummy assignments to handle Python dynamic variable creation 

391 self.dummy_grfn_assignments = [] 

392 

393 def to_dict(self): 

394 result = super().to_dict() 

395 result["name"] = self.name.to_dict() 

396 result["func_args"] = [arg.to_dict() for arg in self.func_args] 

397 result["body"] = [node.to_dict() for node in self.body] 

398 result["con_scope"] = self.con_scope 

399 result["has_ret_val"] = self.has_ret_val 

400 # FUTURE: add attributes to enhance test coverage 

401 return result 

402 

403 def equiv(self, other): 

404 if not isinstance(other, AnnCastFunctionDef): 

405 return False 

406 return self.to_dict() == other.to_dict() 

407 

408 def __str__(self): 

409 return FunctionDef.__str__(self) 

410 

411class AnnCastRecordDef(AnnCastNode): 

412 def __init__(self, name, bases, funcs, fields, source_refs): 

413 super().__init__(self) 

414 self.name = name 

415 self.bases = bases 

416 self.funcs = funcs 

417 self.fields = fields 

418 self.source_refs = source_refs 

419 

420 def to_dict(self): 

421 result = super().to_dict() 

422 # FUTURE: add attributes to result 

423 return result 

424 

425 def equiv(self, other): 

426 if not isinstance(other, AnnCastRecordDef): 

427 return False 

428 return self.to_dict() == other.to_dict() 

429 

430 def __str__(self): 

431 return RecordDef.__str__(self) 

432 

433 

434class AnnCastLiteralValue(AnnCastNode): 

435 def __init__(self, value_type, value, source_code_data_type, source_refs): 

436 super().__init__(self) 

437 self.value_type = value_type 

438 self.value = value 

439 self.source_code_data_type = source_code_data_type 

440 self.source_refs = source_refs 

441 

442 def to_dict(self): 

443 result = super().to_dict() 

444 return result 

445 

446 def equiv(self, other): 

447 if not isinstance(other, AnnCastLiteralValue): 

448 return False 

449 return self.to_dict() == other.to_dict() 

450 

451 def __str__(self): 

452 return CASTLiteralValue.__str__(self) 

453 

454 

455class AnnCastLoop(AnnCastNode): 

456 def __init__(self, pre, expr, body, post, source_refs): 

457 super().__init__(self) 

458 self.pre = pre 

459 self.expr = expr 

460 self.body = body 

461 self.post = post 

462 self.source_refs = source_refs 

463 

464 # Loop container scope 

465 self.con_scope: typing.List 

466 # Function scopestr this Loop node is "living" in 

467 self.base_func_scopestr: str = "" 

468 

469 # dicts mapping a Name id to its string name 

470 # used for container interfaces 

471 self.modified_vars: typing.Dict[int, str] 

472 self.vars_accessed_before_mod: typing.Dict[int, str] 

473 self.used_vars: typing.Dict[int, str] 

474 self.top_interface_vars: typing.Dict[int, str] = {} 

475 self.top_interface_updated_vars: typing.Dict[int, str] = {} 

476 self.bot_interface_vars: typing.Dict[int, str] = {} 

477 

478 # dicts mapping Name id to highest version at end of "block" 

479 self.pre_highest_var_vers = {} 

480 self.expr_highest_var_vers = {} 

481 self.body_highest_var_vers = {} 

482 self.post_highest_var_vers = {} 

483 

484 # dicts mapping a Name id to variable string name 

485 # for variables used in the Loop expr 

486 self.expr_vars_accessed_before_mod = {} 

487 self.expr_modified_vars = {} 

488 self.expr_used_vars = {} 

489 

490 # dicts mapping a Name id to its fullid 

491 # initial versions for the top interface come from enclosing scope 

492 # updated versions for the top interface are versions 

493 # at the bottom of the loop after one or more executions of the loop 

494 self.top_interface_initial = {} 

495 self.top_interface_updated = {} 

496 self.top_interface_out = {} 

497 self.bot_interface_in = {} 

498 self.bot_interface_out = {} 

499 self.condition_in = {} 

500 self.condition_out = {} 

501 # GrFN VariableNode for the condition node 

502 self.condition_var = None 

503 

504 # GrFN lambda expressions 

505 self.top_interface_lambda: str 

506 self.bot_interface_lambda: str 

507 self.condition_lambda: str 

508 # metadata attributes 

509 self.grfn_con_src_ref: GrfnContainerSrcRef 

510 

511 def to_dict(self): 

512 result = super().to_dict() 

513 # result["init"] = [node.to_dict() for node in self.init] 

514 result["expr"] = self.expr.to_dict() 

515 result["body"] = [node.to_dict() for node in self.body] 

516 result["con_scope"] = self.con_scope 

517 result["base_func_scopestr"] = self.base_func_scopestr 

518 # FUTURE: add attributes to enhance test coverage 

519 return result 

520 

521 def equiv(self, other): 

522 if not isinstance(other, AnnCastLoop): 

523 return False 

524 return self.to_dict() == other.to_dict() 

525 

526 def __str__(self): 

527 return Loop.__str__(self) 

528 

529class AnnCastGoto(AnnCastNode): 

530 def __init__(self, expr, label, source_refs): 

531 super().__init__(self) 

532 self.expr = expr 

533 self.label = label 

534 self.source_refs = source_refs 

535 

536 def to_dict(self): 

537 result = super().to_dict() 

538 result["expr"] = self.expr.to_dict() if self.expr != None else "" 

539 result["label"] = self.label 

540 return result 

541 

542 def equiv(self, other): 

543 if not isinstance(other, AnnCastGoto): 

544 return False 

545 return self.to_dict() == other.to_dict() 

546 

547 def __str__(self): 

548 return Goto.__str__(self) 

549 

550class AnnCastLabel(AnnCastNode): 

551 def __init__(self, label, source_refs): 

552 super().__init__(self) 

553 self.label = label 

554 self.source_refs = source_refs 

555 

556 def to_dict(self): 

557 result = super().to_dict() 

558 result["label"] = self.label 

559 return result 

560 

561 def equiv(self, other): 

562 if not isinstance(other, AnnCastLabel): 

563 return False 

564 return self.to_dict() == other.to_dict() 

565 

566 def __str__(self): 

567 return Label.__str__(self) 

568 

569class AnnCastModelBreak(AnnCastNode): 

570 def __init__(self, source_refs): 

571 super().__init__(self) 

572 self.source_refs = source_refs 

573 

574 def to_dict(self): 

575 result = super().to_dict() 

576 return result 

577 

578 def equiv(self, other): 

579 if not isinstance(other, AnnCastModelBreak): 

580 return False 

581 return self.to_dict() == other.to_dict() 

582 

583 def __str__(self): 

584 return ModelBreak.__str__(self) 

585 

586 

587class AnnCastModelContinue(AnnCastNode): 

588 def __init__(self, node: ModelContinue): 

589 super().__init__(self) 

590 self.source_refs = node.source_refs 

591 

592 def to_dict(self): 

593 result = super().to_dict() 

594 return result 

595 

596 def equiv(self, other): 

597 if not isinstance(other, AnnCastModelContinue): 

598 return False 

599 return self.to_dict() == other.to_dict() 

600 

601 def __str__(self): 

602 return ModelContinue.__str__(self) 

603 

604 

605class AnnCastModelImport(AnnCastNode): 

606 def __init__(self, node: ModelImport): 

607 super().__init__(self) 

608 self.name = node.name 

609 self.alias = node.alias 

610 self.symbol = node.symbol 

611 self.all = node.all 

612 self.source_refs = node.source_refs 

613 

614 def to_dict(self): 

615 result = super().to_dict() 

616 return result 

617 

618 def equiv(self, other): 

619 if not isinstance(other, AnnCastModelImport): 

620 return False 

621 return self.to_dict() == other.to_dict() 

622 

623 def __str__(self): 

624 return ModelImport.__str__(self) 

625 

626 

627class AnnCastModelIf(AnnCastNode): 

628 def __init__(self, expr, body, orelse, source_refs): 

629 super().__init__(self) 

630 self.expr = expr 

631 self.body = body 

632 self.orelse = orelse 

633 self.source_refs = source_refs 

634 

635 # ModelIf container scope 

636 self.con_scope: typing.List 

637 # Function scopestr this ModelIf node is "living" in 

638 self.base_func_scopestr: str = "" 

639 

640 # dicts mapping a Name id to string name 

641 # used for container interfaces 

642 self.modified_vars: typing.Dict[int, str] 

643 self.vars_accessed_before_mod: typing.Dict[int, str] 

644 self.used_vars: typing.Dict[int, str] 

645 self.top_interface_vars: typing.Dict[int, str] = {} 

646 self.bot_interface_vars: typing.Dict[int, str] = {} 

647 # dicts mapping a Name id to variable string name 

648 # for variables used in the if expr 

649 self.expr_vars_accessed_before_mod = {} 

650 self.expr_modified_vars = {} 

651 self.expr_used_vars = {} 

652 # dicts mapping Name id to highest version at end of "block" 

653 self.expr_highest_var_vers = {} 

654 self.ifbody_highest_var_vers = {} 

655 self.elsebody_highest_var_vers = {} 

656 

657 # dicts mapping a Name id to its fullid 

658 self.top_interface_in = {} 

659 self.top_interface_out = {} 

660 self.bot_interface_in = {} 

661 self.bot_interface_out = {} 

662 self.condition_in = {} 

663 self.condition_out = {} 

664 self.decision_in = {} 

665 self.decision_out = {} 

666 # GrFN VariableNode for the condition node 

667 self.condition_var = None 

668 

669 # GrFN lambda expressions 

670 self.top_interface_lambda: str 

671 self.bot_interface_lambda: str 

672 self.condition_lambda: str 

673 self.decision_lambda: str 

674 

675 # metadata attributes 

676 self.grfn_con_src_ref: GrfnContainerSrcRef 

677 

678 def to_dict(self): 

679 result = super().to_dict() 

680 result["expr"] = self.expr.to_dict() 

681 result["body"] = [node.to_dict() for node in self.body] 

682 result["orelse"] = [node.to_dict() for node in self.orelse] 

683 result["con_scope"] = self.con_scope 

684 result["base_func_scopestr"] = self.base_func_scopestr 

685 # FUTURE: add attributes to enhance test coverage 

686 return result 

687 

688 def equiv(self, other): 

689 if not isinstance(other, AnnCastModelIf): 

690 return False 

691 return self.to_dict() == other.to_dict() 

692 

693 def __str__(self): 

694 return ModelIf.__str__(self) 

695 

696 

697class AnnCastModelReturn(AnnCastNode): 

698 def __init__(self, value, source_refs): 

699 super().__init__(self) 

700 self.value = value 

701 self.source_refs = source_refs 

702 # cache the FunctionDef node that this return statement lies in 

703 self.owning_func_def: typing.Optional[AnnCastFunctionDef] = None 

704 # store GrfnAssignment for use in GrFN generation 

705 self.grfn_assignment: typing.Optional[GrfnAssignment] = None 

706 

707 def to_dict(self): 

708 result = super().to_dict() 

709 result["value"] = self.value.to_dict() 

710 return result 

711 

712 def equiv(self, other): 

713 if not isinstance(other, AnnCastModelReturn): 

714 return False 

715 return self.to_dict() == other.to_dict() 

716 

717 def __str__(self): 

718 return ModelReturn.__str__(self) 

719 

720 

721class AnnCastModule(AnnCastNode): 

722 def __init__(self, name, body, source_refs): 

723 super().__init__(self) 

724 self.name = name 

725 self.body = body 

726 self.source_refs = source_refs 

727 

728 # dicts mapping a Name id to string name 

729 # used for container interfaces 

730 self.modified_vars: typing.Dict[int, str] = {} 

731 self.vars_accessed_before_mod: typing.Dict[int, str] = {} 

732 self.used_vars: typing.Dict[int, str] = {} 

733 self.con_scope: typing.List 

734 

735 # metadata attributes 

736 self.grfn_con_src_ref: GrfnContainerSrcRef 

737 

738 def to_dict(self): 

739 result = super().to_dict() 

740 result["name"] = str(self.name) 

741 result["body"] = [node.to_dict() for node in self.body] 

742 result["con_scope"] = self.con_scope 

743 # FUTURE: add attributes to enhance test coverage 

744 return result 

745 

746 def equiv(self, other): 

747 if not isinstance(other, AnnCastModule): 

748 return False 

749 return self.to_dict() == other.to_dict() 

750 

751 def __str__(self): 

752 return Module.__str__(self) 

753 

754 

755class AnnCastName(AnnCastNode): 

756 def __init__(self, name, id, source_refs): 

757 super().__init__(self) 

758 self.name = name 

759 self.id = id 

760 self.source_refs = source_refs 

761 # container_scope is used to aid GrFN generation 

762 self.con_scope: typing.List = [] 

763 # Function scopestr this Name node is "living" in 

764 self.base_func_scopestr: str = "" 

765 # versions are bound to the cope of the variable 

766 self.version = None 

767 self.grfn_id = None 

768 

769 def to_dict(self): 

770 result = super().to_dict() 

771 result["name"] = self.name 

772 result["id"] = self.id 

773 result["version"] = self.version 

774 result["con_scope"] = self.con_scope 

775 return result 

776 

777 def equiv(self, other): 

778 if not isinstance(other, AnnCastName): 

779 return False 

780 return self.to_dict() == other.to_dict() 

781 

782 def __str__(self): 

783 return Name.__str__(self) 

784 

785class AnnCastOperator(AnnCastNode): 

786 def __init__(self, source_language, interpreter, version, op, operands, source_refs): 

787 super().__init__(self) 

788 self.source_language = source_language 

789 self.interpreter = interpreter 

790 self.version = version 

791 self.op = op 

792 self.operands = operands 

793 self.source_refs = source_refs 

794 

795 def to_dict(self): 

796 result = super().to_dict() 

797 result["source_language"] = str(self.source_language) 

798 result["interpreter"] = str(self.interpreter) 

799 result["version"] = str(self.version) 

800 result["op"] = str(self.op) 

801 result["operands"] = [operand.to_dict() for operand in self.operands] 

802 return result 

803 

804 def equiv(self, other): 

805 if not isinstance(other, AnnCastOperator): 

806 return False 

807 return self.to_dict() == other.to_dict() 

808 

809 def __str__(self): 

810 return Operator.__str__(self) 

811 

812 

813class AnnCastSet(AnnCastNode): 

814 def __init__(self, values, source_refs): 

815 super().__init__(self) 

816 self.values = values 

817 self.source_refs = source_refs 

818 

819 def to_dict(self): 

820 result = super().to_dict() 

821 # FUTURE: add values to result 

822 return result 

823 

824 def equiv(self, other): 

825 if not isinstance(other, AnnCastSet): 

826 return False 

827 return self.to_dict() == other.to_dict() 

828 

829 def __str__(self): 

830 return Set.__str__(self) 

831 

832 

833class AnnCastTuple(AnnCastNode): 

834 def __init__(self, values, source_refs): 

835 super().__init__(self) 

836 self.values = values 

837 self.source_refs = source_refs 

838 

839 def to_dict(self): 

840 result = super().to_dict() 

841 # FUTURE: add values to result 

842 return result 

843 

844 def equiv(self, other): 

845 if not isinstance(other, AnnCastTuple): 

846 return False 

847 return self.to_dict() == other.to_dict() 

848 

849 def __str__(self): 

850 return Tuple.__str__(self) 

851 

852 

853class AnnCastVar(AnnCastNode): 

854 def __init__(self, val, type, default_value, source_refs): 

855 super().__init__(self) 

856 self.val = val 

857 self.type = type 

858 self.default_value = default_value 

859 self.source_refs = source_refs 

860 

861 def to_dict(self): 

862 result = super().to_dict() 

863 result["val"] = self.val.to_dict() 

864 result["type"] = str(self.type) 

865 result["default_value"] = str(self.default_value) 

866 return result 

867 

868 def equiv(self, other): 

869 if not isinstance(other, AnnCastVar): 

870 return False 

871 return self.to_dict() == other.to_dict() 

872 

873 def __str__(self): 

874 return Var.__str__(self)