Coverage for skema/program_analysis/CAST2FN/ann_cast/variable_version_pass.py: 73%

602 statements  

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

1import typing 

2from functools import singledispatchmethod 

3 

4from skema.model_assembly.metadata import VariableCreationReason, LambdaType 

5from skema.model_assembly.networks import load_lambda_function 

6from skema.program_analysis.CAST2FN.ann_cast.ann_cast_helpers import ( 

7 ELSEBODY, 

8 IFBODY, 

9 IFEXPR, 

10 LOOP_VAR_UPDATED_VERSION, 

11 LOOPPRE, 

12 LOOPBODY, 

13 LOOPEXPR, 

14 LOOPPOST, 

15 VAR_EXIT_VERSION, 

16 VAR_INIT_VERSION, 

17 GrfnAssignment, 

18 add_metadata_from_name_node, 

19 add_metadata_to_grfn_var, 

20 build_fullid, 

21 call_argument_name, 

22 call_container_name, 

23 call_param_name, 

24 call_ret_val_name, 

25 con_scope_to_str, 

26 create_grfn_literal_node, 

27 create_grfn_var, 

28 create_lambda_node_metadata, 

29 func_def_argument_name, 

30 func_def_ret_val_name, 

31 generate_from_source_metadata, 

32 is_func_def_main, 

33 specialized_global_name, 

34) 

35from skema.program_analysis.CAST2FN.ann_cast.annotated_cast import * 

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

37 ScalarType, 

38 StructureType, 

39 ValueConstructor, 

40) 

41 

42 

43class VariableVersionPass: 

44 def __init__(self, pipeline_state: PipelineState): 

45 self.pipeline_state = pipeline_state 

46 self.nodes = self.pipeline_state.nodes 

47 

48 # dict mapping container scopes strs to dicts which 

49 # map Name id to highest version in that container scope 

50 self.con_scope_to_highest_var_vers = {} 

51 

52 for node in self.pipeline_state.nodes: 

53 # when visitor starts, assign_lhs is False 

54 self.visit(node, False) 

55 

56 def init_highest_var_vers_dict(self, con_scopestr, var_ids): 

57 """ 

58 Initialize highest var version dict for scope `con_scopestr` 

59 If the scope is the module, then use a defaultdict starting at zero 

60 otherwise, create a dictionary mapping each of the ids to zero 

61 """ 

62 self.con_scope_to_highest_var_vers[con_scopestr] = {} 

63 for id in var_ids: 

64 self.con_scope_to_highest_var_vers[con_scopestr][id] = 0 

65 # DEBUG printing 

66 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

67 print( 

68 f"initialized highest_vars_vers_dict {self.con_scope_to_highest_var_vers[con_scopestr]}" 

69 ) 

70 

71 def get_highest_ver_in_con_scope(self, con_scopestr, id): 

72 """ 

73 Grab the current version of `id` in scope for `con_scopestr` 

74 Should only be called after `con_scopestr` is in the `self.con_scope_to_highest_var_vers` 

75 """ 

76 return self.con_scope_to_highest_var_vers[con_scopestr][id] 

77 

78 def is_var_in_con_scope(self, con_scopestr: str, id: int): 

79 return id in self.con_scope_to_highest_var_vers[con_scopestr] 

80 

81 def incr_version_in_con_scope( 

82 self, con_scopestr: str, id: int, var_name: str 

83 ): 

84 """ 

85 Grab the next version of `id` in scope for `con_scopestr` 

86 Should only be called after `con_scopestr` is in the `self.con_scope_to_highest_var_vers` 

87 

88 Also creates a GrFN variable for the newly added version 

89 """ 

90 # NOTE: we should have added id to con_scope_to_highest_var_vers when we call 

91 # init_highest_var_vers_dict 

92 # if this does not happen, some logic has failed 

93 assert id in self.con_scope_to_highest_var_vers[con_scopestr] 

94 self.con_scope_to_highest_var_vers[con_scopestr][id] += 1 

95 version = self.con_scope_to_highest_var_vers[con_scopestr][id] 

96 grfn_var = create_grfn_var(var_name, id, version, con_scopestr) 

97 fullid = build_fullid(var_name, id, version, con_scopestr) 

98 self.pipeline_state.store_grfn_var(fullid, grfn_var) 

99 

100 def incr_vars_in_con_scope(self, scopestr, vars): 

101 """ 

102 This will increment all versions of variables in `scopestr` that are 

103 in the dict `vars` which contains variable ids mapped to AnnCastName nodes 

104 """ 

105 for var_id, var_name in vars.items(): 

106 self.incr_version_in_con_scope(scopestr, var_id, var_name) 

107 

108 def add_default_bot_interface_metadata(self, interface_vars): 

109 """ 

110 Adds a bot interface metadata to interface_vars 

111 """ 

112 for fullid in interface_vars.values(): 

113 grfn_var = self.pipeline_state.get_grfn_var(fullid) 

114 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

115 from_source = ( 

116 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

117 ) 

118 from_source_mdata = generate_from_source_metadata( 

119 from_source, VariableCreationReason.BOT_IFACE_INTRO 

120 ) 

121 add_metadata_to_grfn_var( 

122 grfn_var, from_source_mdata=from_source_mdata 

123 ) 

124 

125 def fix_for_python_gcc_declaration_distinction( 

126 self, con_scopestr, id, var_name 

127 ): 

128 """ 

129 This function adds a dummy GrfnAssignment to `None` for variable with id `id` 

130 in the container for con_scopestr 

131 

132 The motivation for this is the difference between how the gcc and Python handle 

133 variable declaration. 

134 

135 For gcc, all variable declaration are placed at the top 

136 of the enclosing FunctionDef. Currently, we rely on this for If and Loop container 

137 top interfaces. 

138 

139 The Python AST follows Python semantics, and variables are introduced inline dynamically. 

140 This leads to many challenges creating interfaces e.g. 

141 

142 ```python 

143 def func(): 

144 x = 5 

145 if x == 5: 

146 z = 3 

147 ``` 

148 In this code example, what happens to z along the else branch? GrFN If containers always make a 

149 selection between two values, but this does not align with dynamic/conditional variable creation in Python, 

150 as in the above code example. 

151 """ 

152 version = self.con_scope_to_highest_var_vers[con_scopestr][id] 

153 # this function should only be called in cases where we need to implement a dummy assignment 

154 # which creates a version 1 variable 

155 assert version == VAR_INIT_VERSION 

156 # increment the version, and create an GrFN variable for the incremented version 

157 self.incr_version_in_con_scope(con_scopestr, id, var_name) 

158 new_version = self.con_scope_to_highest_var_vers[con_scopestr][id] 

159 new_fullid = build_fullid(var_name, id, new_version, con_scopestr) 

160 grfn_var = self.pipeline_state.get_grfn_var(new_fullid) 

161 from_source_mdata = generate_from_source_metadata( 

162 False, VariableCreationReason.DUMMY_ASSIGN 

163 ) 

164 add_metadata_to_grfn_var(grfn_var, from_source_mdata) 

165 

166 # create a literal GrFN assignment for this dummy assignment 

167 assign_metadata = create_lambda_node_metadata(source_refs=[]) 

168 literal_node = create_grfn_literal_node(assign_metadata) 

169 lambda_expr = "lambda: None" 

170 literal_node.func_str = lambda_expr 

171 literal_node.function = load_lambda_function(literal_node.func_str) 

172 dummy_assignment = GrfnAssignment( 

173 literal_node, LambdaType.LITERAL, lambda_expr=lambda_expr 

174 ) 

175 dummy_assignment.outputs[new_fullid] = grfn_var.uid 

176 

177 # add dummy assignment to function def node 

178 assert self.pipeline_state.is_con_scopestr_func_def(con_scopestr) 

179 func_def_node = self.pipeline_state.func_def_node_from_scopestr( 

180 con_scopestr 

181 ) 

182 

183 func_def_node.dummy_grfn_assignments.append(dummy_assignment) 

184 

185 def populate_interface(self, con_scopestr, vars, interface): 

186 """ 

187 Parameters: 

188 - `con_scopestr`: a cached container scope 

189 - `vars`: a dict mapping numerical ids to variable names 

190 - `interface`: a dict mapping numerical variable ids to fullids 

191 (e.g. the top or bottom interface of a container node) 

192 

193 For each variable from `vars`, put the highest version of that variable 

194 from container `con_scopestr` into `interface` 

195 """ 

196 # add vars to interface 

197 for id, var_name in vars.items(): 

198 highest_ver = self.get_highest_ver_in_con_scope(con_scopestr, id) 

199 # if con_scopestr is a FunctionDef container, and highest_ver is VAR_INIT_VERSION 

200 # we call fix_for_python_gcc_declaration_distinction 

201 # this creates a dummy assignment to the variable in the FunctionDef container 

202 # most likely, this is not the ideal long term solution 

203 scopestr_is_func = self.pipeline_state.is_con_scopestr_func_def( 

204 con_scopestr 

205 ) 

206 local_var = ( 

207 scopestr_is_func 

208 and self.pipeline_state.is_var_local_to_func(con_scopestr, id) 

209 ) 

210 if local_var and highest_ver == VAR_INIT_VERSION: 

211 self.fix_for_python_gcc_declaration_distinction( 

212 con_scopestr, id, var_name 

213 ) 

214 # update highest ver after the dummy assignment 

215 highest_ver = self.get_highest_ver_in_con_scope( 

216 con_scopestr, id 

217 ) 

218 fullid = build_fullid(var_name, id, highest_ver, con_scopestr) 

219 interface[id] = fullid 

220 

221 def populate_loop_interfaces(self, node: AnnCastLoop): 

222 # populate interfaces and increment versions in previous scope of modified variables 

223 prev_scopestr = con_scope_to_str(node.con_scope[:-1]) 

224 # populate top interface initial 

225 # these are all used variables 

226 node.top_interface_vars = node.used_vars 

227 self.populate_interface( 

228 prev_scopestr, node.top_interface_vars, node.top_interface_initial 

229 ) 

230 # increment versions of modified vars 

231 self.incr_vars_in_con_scope(prev_scopestr, node.modified_vars) 

232 # populate bot interface out 

233 node.bot_interface_vars = node.modified_vars 

234 self.populate_interface( 

235 prev_scopestr, node.bot_interface_vars, node.bot_interface_out 

236 ) 

237 self.add_default_bot_interface_metadata(node.bot_interface_out) 

238 

239 # populate "inside" of interfaces 

240 con_scopestr = con_scope_to_str(node.con_scope) 

241 # populate top interface updated 

242 # these are all modified variables 

243 node.top_interface_updated_vars = node.modified_vars 

244 for id, var_name in node.top_interface_updated_vars.items(): 

245 version = LOOP_VAR_UPDATED_VERSION 

246 fullid = build_fullid(var_name, id, version, con_scopestr) 

247 node.top_interface_updated[id] = fullid 

248 # populate top interface out 

249 # the top interface chooses between initial and updated versions; 

250 # by convention the produced version is `VAR_INIT_VERSION` 

251 # which is consistent with other containers 

252 for id, var_name in node.top_interface_vars.items(): 

253 version = VAR_INIT_VERSION 

254 fullid = build_fullid(var_name, id, version, con_scopestr) 

255 node.top_interface_out[id] = fullid 

256 # populate bot interface in 

257 # the bot interface takes `VAR_EXIT_VERSION` modified variables 

258 # During GrFN Variable Creation, these versions will be aliased to 

259 # the highest version occuring in the loop expr 

260 for id, var_name in node.bot_interface_vars.items(): 

261 version = VAR_EXIT_VERSION 

262 fullid = build_fullid(var_name, id, version, con_scopestr) 

263 node.bot_interface_in[id] = fullid 

264 

265 def populate_model_if_interfaces(self, node: AnnCastModelIf): 

266 # populate interfaces and increment versions in previous scope of modified variables 

267 prev_scopestr = con_scope_to_str(node.con_scope[:-1]) 

268 # populate top interface in 

269 node.top_interface_vars = node.used_vars 

270 self.populate_interface( 

271 prev_scopestr, node.top_interface_vars, node.top_interface_in 

272 ) 

273 # increment versions 

274 self.incr_vars_in_con_scope(prev_scopestr, node.modified_vars) 

275 # populate bot interface out 

276 node.bot_interface_vars = node.modified_vars 

277 self.populate_interface( 

278 prev_scopestr, node.bot_interface_vars, node.bot_interface_out 

279 ) 

280 self.add_default_bot_interface_metadata(node.bot_interface_out) 

281 

282 # populate "inside" of interfaces 

283 con_scopestr = con_scope_to_str(node.con_scope) 

284 # populate top interface out 

285 # by convention the top interface produces version VAR_INIT_VERSION variables 

286 # and these are propagated to if expr, if body, and else body 

287 for id, var_name in node.top_interface_vars.items(): 

288 version = VAR_INIT_VERSION 

289 fullid = build_fullid(var_name, id, version, con_scopestr) 

290 node.top_interface_out[id] = fullid 

291 # populate bot interface in 

292 # by convention, the bot interface in takes version VAR_EXIT_VERSION variables 

293 # these versions are produced by the Decision node 

294 # and they are created during GrfnVariableCreationPass 

295 for id, var_name in node.bot_interface_vars.items(): 

296 version = VAR_EXIT_VERSION 

297 fullid = build_fullid(var_name, id, version, con_scopestr) 

298 node.bot_interface_in[id] = fullid 

299 

300 def func_def_top_interface_args(self, node: AnnCastFunctionDef): 

301 """ 

302 Creates initial version for each argument and each formal parameter 

303 Links these argument and parameters through the `top_interface_in` and `top_interface_out` 

304 """ 

305 # function container is used to scope parameters 

306 param_con_scopestr = con_scope_to_str(node.con_scope) 

307 # enclosing container is used to scope arguments 

308 enclosing_con_scope = node.con_scope[:-1] 

309 arg_con_scopestr = con_scope_to_str(enclosing_con_scope) 

310 

311 # create argument and parameter variables 

312 # argument variables are inputs to the top interface 

313 # paramter variables are outputs of the top interface 

314 for i, param in enumerate(node.func_args): 

315 # argument name and scope str 

316 arg_name = func_def_argument_name(node, i) 

317 

318 # parameter name and scopestr 

319 assert isinstance(param, AnnCastVar) 

320 param_name = param.val.name 

321 

322 # argument and parameter share id, and start with initial version 

323 id = param.val.id 

324 version = VAR_INIT_VERSION 

325 

326 # build and store GrFN variables for argument and parameter 

327 arg_grfn_var = create_grfn_var( 

328 arg_name, id, version, arg_con_scopestr 

329 ) 

330 arg_fullid = build_fullid(arg_name, id, version, arg_con_scopestr) 

331 self.pipeline_state.store_grfn_var(arg_fullid, arg_grfn_var) 

332 # store arg_fullid 

333 node.arg_index_to_fullid[i] = arg_fullid 

334 # create From Source metadata for the GrFN var 

335 from_source = False 

336 from_source_mdata = generate_from_source_metadata( 

337 from_source, VariableCreationReason.FUNC_ARG 

338 ) 

339 add_metadata_to_grfn_var(arg_grfn_var, from_source_mdata) 

340 

341 param_grfn_var = create_grfn_var( 

342 param_name, id, version, param_con_scopestr 

343 ) 

344 param_fullid = build_fullid( 

345 param_name, id, version, param_con_scopestr 

346 ) 

347 self.pipeline_state.store_grfn_var(param_fullid, param_grfn_var) 

348 # store param_fullid 

349 node.param_index_to_fullid[i] = param_fullid 

350 # store metadata in paramter GrFN Var 

351 add_metadata_from_name_node(param_grfn_var, param.val) 

352 

353 # link argument and parameter through top interface 

354 node.top_interface_in[id] = arg_fullid 

355 node.top_interface_out[id] = param_fullid 

356 

357 # DEBUG printing 

358 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

359 print(f"For FunctionDef {node.name.name}") 

360 print("After func_def_top_iface_args():") 

361 print(f"\ttop_interface_in = {node.top_interface_in}") 

362 print(f"\ttop_interface_out = {node.top_interface_out}") 

363 

364 def func_def_ret_val_creation(self, node: AnnCastFunctionDef): 

365 """ 

366 Creates two GrFN variables for the FunctionDef's return value. 

367 One is in the interior of the container and links 

368 to the bot interface in. The other is outside the container and 

369 links to the bot interface out. 

370 """ 

371 # Create new GrFN for return value for bot interface in and bot interface out 

372 var_name = func_def_ret_val_name(node) 

373 id = self.pipeline_state.next_collapsed_id() 

374 version = VAR_INIT_VERSION 

375 

376 # interior container scope 

377 func_scopestr = con_scope_to_str(node.con_scope) 

378 

379 in_ret_val = create_grfn_var(var_name, id, version, func_scopestr) 

380 in_fullid = build_fullid(var_name, id, version, func_scopestr) 

381 self.pipeline_state.store_grfn_var(in_fullid, in_ret_val) 

382 # create From Source metadata for the GrFN var 

383 from_source = False 

384 from_source_mdata = generate_from_source_metadata( 

385 from_source, VariableCreationReason.FUNC_RET_VAL 

386 ) 

387 add_metadata_to_grfn_var(in_ret_val, from_source_mdata) 

388 

389 # exterior container scope 

390 enclosing_con = node.con_scope[:-1] 

391 enclosing_scopestr = con_scope_to_str(enclosing_con) 

392 out_ret_val = create_grfn_var( 

393 var_name, id, version, enclosing_scopestr 

394 ) 

395 out_fullid = build_fullid(var_name, id, version, enclosing_scopestr) 

396 self.pipeline_state.store_grfn_var(out_fullid, out_ret_val) 

397 # create From Source metadata for the GrFN var 

398 add_metadata_to_grfn_var(out_ret_val, from_source_mdata) 

399 

400 # store created fullid and grfn_id in node's ret_val 

401 node.out_ret_val[id] = out_fullid 

402 node.in_ret_val[id] = in_fullid 

403 # link ret values on bot interface 

404 node.bot_interface_in[id] = in_fullid 

405 node.bot_interface_out[id] = out_fullid 

406 

407 # DEBUG printing 

408 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

409 print(f"For FunctionDef {node.name.name}") 

410 print("\tAfter func_def_ret_val_creation():") 

411 print(f"\ttop_interface_in = {node.top_interface_in}") 

412 print(f"\ttop_interface_out = {node.top_interface_out}") 

413 

414 def add_globals_to_main_func_def_interfaces( 

415 self, node: AnnCastFunctionDef 

416 ): 

417 """ 

418 Populates top and bot interface of main FunctionDef with global variables 

419 - Adds incoming global variable version to top_interface_in 

420 - Increments modified globals versions in enclosing scope 

421 - Adds incremented version to bot_interface_out 

422 - Creates VAR_INIT_VERSION global variables and adds to top_interface_out 

423 - Add `body_highest_var_vers` global variables to bot_interface_in 

424 """ 

425 # in the enclosing scope, increment all versions of global variables 

426 # that are modified by main 

427 enclosing_con_scope = node.con_scope[:-1] 

428 enclosing_scopestr = con_scope_to_str(enclosing_con_scope) 

429 

430 # add globals to exterior interfaces 

431 # add global variables to top_interface_in these are all used globals 

432 node.top_interface_vars = node.used_globals 

433 self.populate_interface( 

434 enclosing_scopestr, node.top_interface_vars, node.top_interface_in 

435 ) 

436 # the bot interface globals are all modified globals 

437 node.bot_interface_vars = node.modified_globals 

438 # increment versions of all modified global variables 

439 self.incr_vars_in_con_scope( 

440 enclosing_scopestr, node.bot_interface_vars 

441 ) 

442 # add modified globals to bot interface out 

443 self.populate_interface( 

444 enclosing_scopestr, node.bot_interface_vars, node.bot_interface_out 

445 ) 

446 

447 # add globals to interior interfaces 

448 # interior container scope 

449 func_scopestr = con_scope_to_str(node.con_scope) 

450 # create globals for top_interface_out and bot interface in 

451 # by convention the top interface produces version VAR_INIT_VERSION variables 

452 # by convention, the bot interface in takes version VAR_EXIT_VERSION variables 

453 for id, var_name in node.top_interface_vars.items(): 

454 version = VAR_INIT_VERSION 

455 init_fullid = build_fullid(var_name, id, version, func_scopestr) 

456 init_global = create_grfn_var(var_name, id, version, func_scopestr) 

457 self.pipeline_state.store_grfn_var(init_fullid, init_global) 

458 node.top_interface_out[id] = init_fullid 

459 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

460 from_source = ( 

461 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

462 ) 

463 from_source_mdata = generate_from_source_metadata( 

464 from_source, VariableCreationReason.TOP_IFACE_INTRO 

465 ) 

466 add_metadata_to_grfn_var(init_global, from_source_mdata) 

467 

468 # we do not create the GrFN VariableNode for the highest version global 

469 # here, since it is done while visitng Assignment node during GrfnVarCreation pass 

470 for id, var_name in node.bot_interface_vars.items(): 

471 version = node.body_highest_var_vers[id] 

472 exit_fullid = build_fullid(var_name, id, version, func_scopestr) 

473 node.bot_interface_in[id] = exit_fullid 

474 

475 # DEBUG printing 

476 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

477 print(f"For FunctionDef {node.name.name}") 

478 print("\tAfter add_globals_to_main_func_def_interfaces():") 

479 print(f"\ttop_interface_in = {node.top_interface_in}") 

480 print(f"\ttop_interface_out = {node.top_interface_out}") 

481 

482 def add_globals_to_non_main_func_def_interfaces( 

483 self, node: AnnCastFunctionDef 

484 ): 

485 """ 

486 Populates top and bot interface of FunctionDef with global variables 

487 For each global, we make an addtional global whose name is specialized to 

488 this function. This is to separate the globals that "main" uses 

489 from the globals that are used in other FunctionDef's because of main's 

490 special role. 

491 - Creates VAR_INIT_VERSION version for each specialized global and 

492 Links these specialized globals through the `top_interface_in` and `top_interface_out` 

493 - Creates VAR_EXIT_VERSION version for each specialized global and 

494 Links these specialized globals through the `bot_interface_in` and `bot_interface_out` 

495 """ 

496 enclosing_con_scope = node.con_scope[:-1] 

497 enclosing_scopestr = con_scope_to_str(enclosing_con_scope) 

498 # interior container scope 

499 func_scopestr = con_scope_to_str(node.con_scope) 

500 

501 # add global variables to top_interface_in 

502 # these are all used globals 

503 node.top_interface_vars = node.used_globals 

504 # the bot interface globals are all modified globals 

505 node.bot_interface_vars = node.modified_globals 

506 

507 # we create specialized globals for this function def, in the enclosing scope. 

508 # this is to accomodate interfaces, since they expect the same id 

509 # on either side 

510 # this is similar to how we handle arguments from enclosing scope linking to 

511 # parameters in the interior of a container 

512 

513 # create specialized globals for top interface 

514 # by convention the top interface produces version VAR_INIT_VERSION variables 

515 version = VAR_INIT_VERSION 

516 for id, var_name in node.top_interface_vars.items(): 

517 # exterior specialized top global 

518 specialized_name = specialized_global_name(node, var_name) 

519 in_fullid = build_fullid( 

520 specialized_name, id, version, enclosing_scopestr 

521 ) 

522 in_global = create_grfn_var( 

523 specialized_name, id, version, enclosing_scopestr 

524 ) 

525 self.pipeline_state.store_grfn_var(in_fullid, in_global) 

526 node.top_interface_in[id] = in_fullid 

527 # create From Source metadata for the GrFN var 

528 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

529 from_source = ( 

530 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

531 ) 

532 from_source_mdata = generate_from_source_metadata( 

533 from_source, VariableCreationReason.DUP_GLOBAL 

534 ) 

535 add_metadata_to_grfn_var(in_global, from_source_mdata) 

536 # interior top global 

537 out_fullid = build_fullid(var_name, id, version, func_scopestr) 

538 out_global = create_grfn_var(var_name, id, version, func_scopestr) 

539 self.pipeline_state.store_grfn_var(out_fullid, out_global) 

540 node.top_interface_out[id] = out_fullid 

541 # create From Source metadata for the GrFN var 

542 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

543 from_source = ( 

544 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

545 ) 

546 from_source_mdata = generate_from_source_metadata( 

547 from_source, VariableCreationReason.TOP_IFACE_INTRO 

548 ) 

549 add_metadata_to_grfn_var(in_global, from_source_mdata) 

550 

551 # create specialized globals for bot interface 

552 # by convention, the bot interface in takes version VAR_EXIT_VERSION variables 

553 for id, var_name in node.bot_interface_vars.items(): 

554 # interior bot global 

555 # we do not create the GrFN VariableNode for the highest version global 

556 # here, since it is done while visitng Assignment node during GrfnVarCreation pass 

557 version = node.body_highest_var_vers[id] 

558 in_fullid = build_fullid(var_name, id, version, func_scopestr) 

559 node.bot_interface_in[id] = in_fullid 

560 # exterior specialized bot global 

561 version = VAR_EXIT_VERSION 

562 specialized_name = specialized_global_name(node, var_name) 

563 out_fullid = build_fullid( 

564 specialized_name, id, version, enclosing_scopestr 

565 ) 

566 out_global = create_grfn_var( 

567 specialized_name, id, version, enclosing_scopestr 

568 ) 

569 self.pipeline_state.store_grfn_var(out_fullid, out_global) 

570 node.bot_interface_out[id] = out_fullid 

571 # create From Source metadata for the GrFN var 

572 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

573 from_source = ( 

574 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

575 ) 

576 from_source_mdata = generate_from_source_metadata( 

577 from_source, VariableCreationReason.DUP_GLOBAL 

578 ) 

579 add_metadata_to_grfn_var(out_global, from_source_mdata) 

580 

581 # DEBUG printing 

582 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

583 print(f"For FunctionDef {node.name.name}") 

584 print("\tAfter add_globals_to_non_main_func_def_interfaces():") 

585 print(f"\ttop_interface_in = {node.top_interface_in}") 

586 print(f"\ttop_interface_out = {node.top_interface_out}") 

587 print(f"\ttop_interface_vars = {node.top_interface_vars}") 

588 print(f"\tbot_interface_in = {node.bot_interface_in}") 

589 print(f"\tbot_interface_out = {node.bot_interface_out}") 

590 print(f"\tbot_interface_vars = {node.bot_interface_vars}") 

591 

592 def call_top_interface_args_with_func_def(self, node: AnnCastCall): 

593 """ 

594 Creates initial version for each argument and each formal parameter 

595 Links these argument and parameters through the `top_interface_in` and `top_interface_out` 

596 

597 During GrfnAssignmentPass, 

598 for each argument, creates a `GrfnAssignment` which stores the assignment `LambdaNode` 

599 """ 

600 # call container is used to scope parameters 

601 call_con_name = call_container_name(node) 

602 

603 # create argument and parameter variables 

604 # argument variables are inputs to the top interface 

605 # paramter variables are outputs of the top interface 

606 for i, n in enumerate(node.arguments): 

607 # parameter name and scopestr 

608 func_def = self.pipeline_state.func_def_node_from_id(node.func.id) 

609 if i < len(func_def.func_args): # NOTE: M7 Placeholder 

610 # argument name and scope str 

611 arg_name = call_argument_name(node, i) 

612 arg_con_scopestr = con_scope_to_str(node.func.con_scope) 

613 

614 param = func_def.func_args[i] 

615 assert isinstance(param, AnnCastVar) 

616 param_name = param.val.name 

617 param_con_scopestr = con_scope_to_str( 

618 node.func.con_scope + [call_con_name] 

619 ) 

620 

621 # argument and parameter share id, and start with initial version 

622 id = self.pipeline_state.next_collapsed_id() 

623 version = VAR_INIT_VERSION 

624 

625 # build and store GrFN variables for argument and parameter 

626 arg_grfn_var = create_grfn_var( 

627 arg_name, id, version, arg_con_scopestr 

628 ) 

629 arg_fullid = build_fullid( 

630 arg_name, id, version, arg_con_scopestr 

631 ) 

632 self.pipeline_state.store_grfn_var(arg_fullid, arg_grfn_var) 

633 # store arg_fullid 

634 node.arg_index_to_fullid[i] = arg_fullid 

635 # create From Source metadata for the GrFN var 

636 from_source = False 

637 from_source_mdata = generate_from_source_metadata( 

638 from_source, VariableCreationReason.FUNC_ARG 

639 ) 

640 add_metadata_to_grfn_var(arg_grfn_var, from_source_mdata) 

641 

642 param_grfn_var = create_grfn_var( 

643 param_name, id, version, param_con_scopestr 

644 ) 

645 param_fullid = build_fullid( 

646 param_name, id, version, param_con_scopestr 

647 ) 

648 self.pipeline_state.store_grfn_var( 

649 param_fullid, param_grfn_var 

650 ) 

651 # store param_fullid 

652 node.param_index_to_fullid[i] = param_fullid 

653 # create From Source metadata for the GrFN var 

654 add_metadata_from_name_node(param_grfn_var, param.val) 

655 

656 # link argument and parameter through top interface 

657 node.top_interface_in[id] = arg_fullid 

658 node.top_interface_out[id] = param_fullid 

659 

660 # DEBUG printing 

661 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

662 print("After create_call_args_and_params():") 

663 print(f"\ttop_interface_in = {node.top_interface_in}") 

664 print(f"\ttop_interface_out = {node.top_interface_out}") 

665 

666 def call_top_interface_args_with_no_func_def(self, node: AnnCastCall): 

667 """ 

668 Creates initial version for each argument and each formal parameter 

669 Links these argument and parameters through the `top_interface_in` and `top_interface_out` 

670 

671 During GrfnAssignmentPass, 

672 for each argument, creates a `GrfnAssignment` which stores the assignment `LambdaNode` 

673 """ 

674 # call container is used to scope parameters 

675 call_con_name = call_container_name(node) 

676 

677 # create argument and parameter variables 

678 # argument variables are inputs to the top interface 

679 # paramter variables are outputs of the top interface 

680 for i, n in enumerate(node.arguments): 

681 # argument name and scope str 

682 arg_name = call_argument_name(node, i) 

683 arg_con_scopestr = con_scope_to_str(node.func.con_scope) 

684 

685 # parameter name and scopestr 

686 param_name = call_param_name(node, i) 

687 param_con_scopestr = con_scope_to_str( 

688 node.func.con_scope + [call_con_name] 

689 ) 

690 

691 # argument and parameter share id, and start with initial version 

692 id = self.pipeline_state.next_collapsed_id() 

693 version = VAR_INIT_VERSION 

694 

695 # build and store GrFN variables for argument and parameter 

696 arg_grfn_var = create_grfn_var( 

697 arg_name, id, version, arg_con_scopestr 

698 ) 

699 arg_fullid = build_fullid(arg_name, id, version, arg_con_scopestr) 

700 self.pipeline_state.store_grfn_var(arg_fullid, arg_grfn_var) 

701 # store arg_fullid 

702 node.arg_index_to_fullid[i] = arg_fullid 

703 # create From Source metadata for the GrFN var 

704 from_source = False 

705 from_source_mdata = generate_from_source_metadata( 

706 from_source, VariableCreationReason.FUNC_ARG 

707 ) 

708 add_metadata_to_grfn_var(arg_grfn_var, from_source_mdata) 

709 

710 param_grfn_var = create_grfn_var( 

711 param_name, id, version, param_con_scopestr 

712 ) 

713 param_fullid = build_fullid( 

714 param_name, id, version, param_con_scopestr 

715 ) 

716 self.pipeline_state.store_grfn_var(param_fullid, param_grfn_var) 

717 # store param_fullid 

718 node.param_index_to_fullid[i] = param_fullid 

719 # create From Source metadata for the GrFN var 

720 # when we don't have the function def, we create a paramter with a default name 

721 add_metadata_to_grfn_var(param_grfn_var, from_source_mdata) 

722 

723 # link argument and parameter through top interface 

724 node.top_interface_in[id] = arg_fullid 

725 node.top_interface_out[id] = param_fullid 

726 

727 # DEBUG printing 

728 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

729 print("After create_call_args_and_params():") 

730 print(f"\ttop_interface_in = {node.top_interface_in}") 

731 print(f"\ttop_interface_out = {node.top_interface_out}") 

732 

733 def populate_call_bot_interface_with_ret_val(self, node: AnnCastCall): 

734 """ 

735 Creates two GrFN variables for the Call's return value. 

736 One is in the interior of the container and links 

737 to the bot interface in. The other is outside the container and 

738 links to the bot interface out. 

739 """ 

740 # Create new GrFN for return value for bot interface in and bot interface out 

741 var_name = call_ret_val_name(node) 

742 id = self.pipeline_state.next_collapsed_id() 

743 version = VAR_INIT_VERSION 

744 

745 # interior container scope 

746 call_con_scopestr = con_scope_to_str( 

747 node.func.con_scope + [call_container_name(node)] 

748 ) 

749 

750 in_ret_val = create_grfn_var(var_name, id, version, call_con_scopestr) 

751 in_fullid = build_fullid(var_name, id, version, call_con_scopestr) 

752 self.pipeline_state.store_grfn_var(in_fullid, in_ret_val) 

753 # create From Source metadata for the GrFN var 

754 from_source = False 

755 from_source_mdata = generate_from_source_metadata( 

756 from_source, VariableCreationReason.FUNC_RET_VAL 

757 ) 

758 add_metadata_to_grfn_var(in_ret_val, from_source_mdata) 

759 

760 # exterior container scope 

761 con_scopestr = con_scope_to_str(node.func.con_scope) 

762 out_ret_val = create_grfn_var(var_name, id, version, con_scopestr) 

763 out_fullid = build_fullid(var_name, id, version, con_scopestr) 

764 self.pipeline_state.store_grfn_var(out_fullid, out_ret_val) 

765 add_metadata_to_grfn_var(out_ret_val, from_source_mdata) 

766 

767 # store created fullid and grfn_id in node's ret_val 

768 node.out_ret_val[id] = out_fullid 

769 node.in_ret_val[id] = in_fullid 

770 # link ret values on bot interface 

771 node.bot_interface_in[id] = in_fullid 

772 node.bot_interface_out[id] = out_fullid 

773 

774 def grfn_2_2_call_top_interface_args(self, node: AnnCastCall): 

775 """ 

776 Creates initial version for each argument and each formal parameter 

777 Links these argument and parameters through the `top_interface_in` and `top_interface_out` 

778 

779 During GrfnAssignmentPass, 

780 for each argument, creates a `GrfnAssignment` which stores the assignment `LambdaNode` 

781 """ 

782 # call container is used to scope parameters 

783 call_con_name = call_container_name(node) 

784 

785 # create argument and parameter variables 

786 # argument variables are inputs to the top interface 

787 # paramter variables are outputs of the top interface 

788 # if we are generating GrFN 2.2, we would like the parameter to lie in the 

789 # copied function def container, we do this by aliasing versions during GrfnVarCreation pass 

790 for i, n in enumerate(node.arguments): 

791 # argument name and scope str 

792 arg_name = call_argument_name(node, i) 

793 arg_con_scopestr = con_scope_to_str(node.func.con_scope) 

794 

795 # parameter name and scopestr 

796 param = node.func_def_copy.func_args[i] 

797 assert isinstance(param, AnnCastVar) 

798 param_name = param.val.name 

799 param_con_scopestr = con_scope_to_str( 

800 node.func.con_scope + [call_con_name] 

801 ) 

802 

803 # argument and parameter share id, and start with initial version 

804 id = self.pipeline_state.next_collapsed_id() 

805 version = VAR_INIT_VERSION 

806 

807 # build and store GrFN variables for argument and parameter 

808 arg_grfn_var = create_grfn_var( 

809 arg_name, id, version, arg_con_scopestr 

810 ) 

811 arg_fullid = build_fullid(arg_name, id, version, arg_con_scopestr) 

812 self.pipeline_state.store_grfn_var(arg_fullid, arg_grfn_var) 

813 # store arg_fullid 

814 node.arg_index_to_fullid[i] = arg_fullid 

815 # create From Source metadata for the GrFN var 

816 from_source = False 

817 from_source_mdata = generate_from_source_metadata( 

818 from_source, VariableCreationReason.FUNC_ARG 

819 ) 

820 add_metadata_to_grfn_var(arg_grfn_var, from_source_mdata) 

821 

822 param_grfn_var = create_grfn_var( 

823 param_name, id, version, param_con_scopestr 

824 ) 

825 param_fullid = build_fullid( 

826 param_name, id, version, param_con_scopestr 

827 ) 

828 self.pipeline_state.store_grfn_var(param_fullid, param_grfn_var) 

829 # store param_fullid 

830 node.param_index_to_fullid[i] = param_fullid 

831 add_metadata_from_name_node(param_grfn_var, param.val) 

832 

833 # link argument and parameter through top interface 

834 node.top_interface_in[id] = arg_fullid 

835 node.top_interface_out[id] = param_fullid 

836 

837 # DEBUG printing 

838 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

839 print("After create_call_args_and_params():") 

840 print(f"\ttop_interface_in = {node.top_interface_in}") 

841 print(f"\ttop_interface_out = {node.top_interface_out}") 

842 

843 def grfn_2_2_call_ret_val_creation(self, node: AnnCastCall): 

844 """ 

845 Creates two GrFN variables for the Call's return value. 

846 One is in the interior of the container and links 

847 to the bot interface in. The other is outside the container and 

848 links to the bot interface out. 

849 """ 

850 # Create new GrFN for return value for bot interface in and bot interface out 

851 var_name = call_ret_val_name(node) 

852 id = self.pipeline_state.next_collapsed_id() 

853 version = VAR_INIT_VERSION 

854 

855 # interior container scope 

856 call_con_scopestr = con_scope_to_str( 

857 node.func.con_scope + [call_container_name(node)] 

858 ) 

859 

860 in_ret_val = create_grfn_var(var_name, id, version, call_con_scopestr) 

861 in_fullid = build_fullid(var_name, id, version, call_con_scopestr) 

862 self.pipeline_state.store_grfn_var(in_fullid, in_ret_val) 

863 # create From Source metadata for the GrFN var 

864 from_source = False 

865 from_source_mdata = generate_from_source_metadata( 

866 from_source, VariableCreationReason.FUNC_RET_VAL 

867 ) 

868 add_metadata_to_grfn_var(in_ret_val, from_source_mdata) 

869 

870 # exterior container scope 

871 con_scopestr = con_scope_to_str(node.func.con_scope) 

872 out_ret_val = create_grfn_var(var_name, id, version, con_scopestr) 

873 out_fullid = build_fullid(var_name, id, version, con_scopestr) 

874 self.pipeline_state.store_grfn_var(out_fullid, out_ret_val) 

875 # create From Source metadata for the GrFN var 

876 add_metadata_to_grfn_var(out_ret_val, from_source_mdata) 

877 

878 # store created fullid and grfn_id in node's ret_val 

879 node.out_ret_val[id] = out_fullid 

880 node.in_ret_val[id] = in_fullid 

881 # link ret values on bot interface 

882 node.bot_interface_in[id] = in_fullid 

883 node.bot_interface_out[id] = out_fullid 

884 

885 # also, store the created ret_val in the copied function def 

886 # this is done so that we can assign to the ret val when 

887 # parsing return statements 

888 node.func_def_copy.in_ret_val[id] = in_fullid 

889 

890 def add_globals_to_grfn_2_2_call_interfaces(self, node: AnnCastCall): 

891 """ 

892 Populates top and bot interface with global variables 

893 - Adds incoming global variable version to top_interface_in 

894 - Increments modified globals versions in enclosing scope 

895 - Adds incremented version to bot_interface_out 

896 - Creates VAR_INIT_VERSION global variables in Call contianer scope and 

897 adds them to top_interface_out 

898 - Creates VAR_INIT_VERSION global variables in copied FunctionDef scope and 

899 aliases them to their corresponding Call container scope globals 

900 - Creates VAR_EXIT_VERSION global variables and adds to bot_interface_in 

901 """ 

902 # in the current scope, increment all versions of global variables 

903 # that are modified by this call 

904 # the calling container scope is stored in the Call's AnnCastName node 

905 calling_scopestr = con_scope_to_str(node.func.con_scope) 

906 

907 # add globals to exterior interfaces 

908 # add global variables to top_interface_in 

909 # these are all used globals 

910 node.top_interface_vars = node.func_def_copy.used_globals 

911 self.populate_interface( 

912 calling_scopestr, node.top_interface_vars, node.top_interface_in 

913 ) 

914 # the bot interface globals are all modified globals 

915 node.bot_interface_vars = node.func_def_copy.modified_globals 

916 # increment versions of all modified global variables 

917 self.incr_vars_in_con_scope(calling_scopestr, node.bot_interface_vars) 

918 # add modified globals to bot interface out 

919 self.populate_interface( 

920 calling_scopestr, node.bot_interface_vars, node.bot_interface_out 

921 ) 

922 

923 # add globals to interior interfaces 

924 # interior container scope 

925 call_con_scopestr = con_scope_to_str( 

926 node.func.con_scope + [call_container_name(node)] 

927 ) 

928 copied_func_scopestr = con_scope_to_str(node.func_def_copy.con_scope) 

929 # create globals for top_interface_out and bot interface in 

930 # by convention the top interface produces version VAR_INIT_VERSION variables 

931 # by convention, the bot interface in takes version VAR_EXIT_VERSION variables 

932 for id, var_name in node.top_interface_vars.items(): 

933 version = VAR_INIT_VERSION 

934 call_init_fullid = build_fullid( 

935 var_name, id, version, call_con_scopestr 

936 ) 

937 call_init_global = create_grfn_var( 

938 var_name, id, version, call_con_scopestr 

939 ) 

940 self.pipeline_state.store_grfn_var( 

941 call_init_fullid, call_init_global 

942 ) 

943 node.top_interface_out[id] = call_init_fullid 

944 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

945 from_source = ( 

946 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

947 ) 

948 from_source_mdata = generate_from_source_metadata( 

949 from_source, VariableCreationReason.TOP_IFACE_INTRO 

950 ) 

951 add_metadata_to_grfn_var(call_init_global, from_source_mdata) 

952 

953 # alias the func copies init version 

954 func_copy_init_fullid = build_fullid( 

955 var_name, id, version, copied_func_scopestr 

956 ) 

957 self.pipeline_state.alias_grfn_vars( 

958 func_copy_init_fullid, call_init_fullid 

959 ) 

960 

961 for id, var_name in node.bot_interface_vars.items(): 

962 version = VAR_EXIT_VERSION 

963 exit_fullid = build_fullid( 

964 var_name, id, version, call_con_scopestr 

965 ) 

966 exit_global = create_grfn_var( 

967 var_name, id, version, call_con_scopestr 

968 ) 

969 self.pipeline_state.store_grfn_var(exit_fullid, exit_global) 

970 node.bot_interface_in[id] = exit_fullid 

971 # we intentionally do not add metadata to the GrFN variable here, since 

972 # the highest version from the copied FunctionDef will be aliased to this 

973 # variable, and the metadata for this GrFN variable will be populated from 

974 # that highest version 

975 

976 # DEBUG printing 

977 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

978 print("After adding globals for GrFN 2.2 call ():") 

979 print(f"\ttop_interface_in = {node.top_interface_in}") 

980 print(f"\tbot_interface_out = {node.bot_interface_out}") 

981 

982 def add_globals_to_call_interfaces(self, node: AnnCastCall): 

983 """ 

984 Populates top and bot interface with global variables 

985 - Adds incoming global variable version to top_interface_in 

986 - Increments modified globals versions in enclosing scope 

987 - Adds incremented version to bot_interface_out 

988 - Creates VAR_INIT_VERSION global variables and adds to top_interface_out 

989 - Creates VAR_EXIT_VERSION global variables and adds to bot_interface_in 

990 """ 

991 # in the current scope, increment all versions of global variables 

992 # that are modified by this call 

993 # the calling container scope is stored in the Call's AnnCastName node 

994 calling_scopestr = con_scope_to_str(node.func.con_scope) 

995 func_def = self.pipeline_state.func_def_node_from_id(node.func.id) 

996 

997 # add globals to exterior interfaces 

998 # top interface globals are globals which are accessed before modification 

999 node.top_interface_vars = func_def.used_globals 

1000 

1001 # add global variables to top_interface_in 

1002 self.populate_interface( 

1003 calling_scopestr, node.top_interface_vars, node.top_interface_in 

1004 ) 

1005 # the bot interface globals are all modified globals 

1006 node.bot_interface_vars = func_def.modified_globals 

1007 # increment versions of all modified global variables 

1008 self.incr_vars_in_con_scope(calling_scopestr, node.bot_interface_vars) 

1009 # add modified globals to bot interface out 

1010 self.populate_interface( 

1011 calling_scopestr, node.bot_interface_vars, node.bot_interface_out 

1012 ) 

1013 

1014 # add globals to interior interfaces 

1015 # interior container scope 

1016 call_con_scopestr = con_scope_to_str( 

1017 node.func.con_scope + [call_container_name(node)] 

1018 ) 

1019 # create globals for top_interface_out and bot interface in 

1020 # by convention the top interface produces version VAR_INIT_VERSION variables 

1021 # by convention, the bot interface in takes version VAR_EXIT_VERSION variables 

1022 for id, var_name in node.top_interface_vars.items(): 

1023 version = VAR_INIT_VERSION 

1024 init_fullid = build_fullid( 

1025 var_name, id, version, call_con_scopestr 

1026 ) 

1027 init_global = create_grfn_var( 

1028 var_name, id, version, call_con_scopestr 

1029 ) 

1030 self.pipeline_state.store_grfn_var(init_fullid, init_global) 

1031 node.top_interface_out[id] = init_fullid 

1032 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

1033 from_source = ( 

1034 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

1035 ) 

1036 from_source_mdata = generate_from_source_metadata( 

1037 from_source, VariableCreationReason.TOP_IFACE_INTRO 

1038 ) 

1039 add_metadata_to_grfn_var(init_global, from_source_mdata) 

1040 

1041 for id, var_name in node.bot_interface_vars.items(): 

1042 version = VAR_EXIT_VERSION 

1043 exit_fullid = build_fullid( 

1044 var_name, id, version, call_con_scopestr 

1045 ) 

1046 exit_global = create_grfn_var( 

1047 var_name, id, version, call_con_scopestr 

1048 ) 

1049 self.pipeline_state.store_grfn_var(exit_fullid, exit_global) 

1050 node.bot_interface_in[id] = exit_fullid 

1051 # See comment above declaration for `FROM_SOURCE_FOR_GE` in annotated_cast.py 

1052 from_source = ( 

1053 True if self.pipeline_state.FROM_SOURCE_FOR_GE else False 

1054 ) 

1055 from_source_mdata = generate_from_source_metadata( 

1056 from_source, VariableCreationReason.BOT_IFACE_INTRO 

1057 ) 

1058 add_metadata_to_grfn_var(exit_global, from_source_mdata) 

1059 

1060 # DEBUG printing 

1061 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

1062 print("After adding globals for GrFN 2.3 call ():") 

1063 print(f"\ttop_interface_in = {node.top_interface_in}") 

1064 print(f"\tbot_interface_out = {node.bot_interface_out}") 

1065 

1066 def visit(self, node: AnnCastNode, assign_lhs: bool): 

1067 # print current node being visited. 

1068 # this can be useful for debugging 

1069 # class_name = node.__class__.__name__ 

1070 # print(f"\nProcessing node type {class_name}") 

1071 return self._visit(node, assign_lhs) 

1072 

1073 @singledispatchmethod 

1074 def _visit(self, node: AnnCastNode, assign_lhs: bool): 

1075 """ 

1076 Visit each AnnCastNode 

1077 Parameters: 

1078 - `assign_lhs`: this denotes whether we are visiting the LHS or RHS of an AnnCastAssignment 

1079 This is used to determine whether a variable (AnnCastName node) is 

1080 accessed or modified in that context 

1081 """ 

1082 raise Exception(f"Unimplemented AST node of type: {type(node)}") 

1083 

1084 def visit_node_list(self, node_list: typing.List[AnnCastNode], assign_lhs): 

1085 return [self.visit(node, assign_lhs) for node in node_list] 

1086 

1087 @_visit.register 

1088 def visit_assignment(self, node: AnnCastAssignment, assign_lhs: bool): 

1089 self.visit(node.right, assign_lhs) 

1090 # The AnnCastTuple is added to handle scenarios where an assignment 

1091 # is made by assigning to a tuple of values, as opposed to one singular value 

1092 assert ( 

1093 isinstance(node.left, AnnCastVar) 

1094 or (isinstance(node.left, AnnCastLiteralValue) and (node.left.value_type == StructureType.TUPLE)) 

1095 or isinstance(node.left, AnnCastAssignment) 

1096 or isinstance(node.left, AnnCastAttribute) or isinstance(node.left, AnnCastCall) 

1097 ), f"container_scope: visit_assigment: node.left is {type(node.left)}" 

1098 self.visit(node.left, True) 

1099 

1100 @_visit.register 

1101 def visit_attribute(self, node: AnnCastAttribute, assign_lhs: bool): 

1102 pass 

1103 

1104 @_visit.register 

1105 def visit_call(self, node: AnnCastCall, assign_lhs: bool): 

1106 assert isinstance(node.func, AnnCastName) or isinstance( 

1107 node.func, AnnCastAttribute 

1108 ) 

1109 

1110 if node.is_grfn_2_2: 

1111 self.visit_call_grfn_2_2(node, assign_lhs) 

1112 return 

1113 

1114 self.visit_node_list(node.arguments, assign_lhs) 

1115 # populate call nodes's top interface with arguments 

1116 # The pattern for the top interface is as follows: 

1117 # For each argument, we create a GrFN variable using the arguments index 

1118 # E.g. Arg0, Arg1, ... 

1119 # top interface inputs: Arg0, Arg1,... 

1120 # top interface outputs: Param0, Param1, ... 

1121 # if this Call has a FunctionDef, then we can fill in correct paramter names 

1122 # if it doesn't we need to provide default parameter names 

1123 # if we have the FunctionDef for the call, we can also add globals to the interfaces 

1124 if node.has_func_def: 

1125 self.call_top_interface_args_with_func_def(node) 

1126 self.add_globals_to_call_interfaces(node) 

1127 func_node = node.func.id 

1128 func_def_node = self.pipeline_state.func_def_node_from_id( 

1129 func_node 

1130 ) 

1131 node.has_ret_val = func_def_node.has_ret_val 

1132 # if we do not have the FunctionDef, we will not add any globals to the interfaces 

1133 else: 

1134 self.call_top_interface_args_with_no_func_def(node) 

1135 

1136 # add return value to bot interface out 

1137 if node.has_ret_val: 

1138 self.populate_call_bot_interface_with_ret_val(node) 

1139 

1140 def visit_call_grfn_2_2(self, node: AnnCastCall, assign_lhs: bool): 

1141 assert isinstance(node.func, AnnCastName) 

1142 self.visit_node_list(node.arguments, assign_lhs) 

1143 # populate call nodes's top interface with arguments 

1144 # The pattern for the top interface is as follows: 

1145 # For each argument, we create a GrFN variable using the arguments index 

1146 # E.g. Arg0, Arg1, ... 

1147 # top interface inputs: Arg0, Arg1,... 

1148 # top interface outputs: NamedParam0, NamedParam1, ... 

1149 self.grfn_2_2_call_top_interface_args(node) 

1150 

1151 node.has_ret_val = node.func_def_copy.has_ret_val 

1152 # add return value to bot interface out if function_copy has a ret_val 

1153 if node.func_def_copy.has_ret_val: 

1154 self.grfn_2_2_call_ret_val_creation(node) 

1155 

1156 # we visit the function def copy to version globals appearing in its body 

1157 call_assign_lhs = False 

1158 self.visit_function_def_copy(node.func_def_copy, call_assign_lhs) 

1159 

1160 # add globals to call interface 

1161 self.add_globals_to_grfn_2_2_call_interfaces(node) 

1162 

1163 @_visit.register 

1164 def visit_record_def(self, node: AnnCastRecordDef, assign_lhs: bool): 

1165 # Visit the function defs within this class to make sure 

1166 # Everything is versioned correctly 

1167 self.visit_node_list(node.funcs, assign_lhs) 

1168 

1169 @_visit.register 

1170 def visit_goto(self, node: AnnCastGoto, assign_lhs): 

1171 if node.expr != None: 

1172 self.visit(node.expr, assign_lhs) 

1173 # self.visit(node.label, at_module_scope, assign_lhs) 

1174 pass 

1175 

1176 @_visit.register 

1177 def visit_label(self, node: AnnCastLabel, assign_lhs): 

1178 # self.visit(node.label, at_module_scope, assign_lhs) 

1179 pass 

1180 

1181 def visit_function_def_copy( 

1182 self, node: AnnCastFunctionDef, assign_lhs: bool 

1183 ): 

1184 """ 

1185 Used for GrFN 2.2 Generation 

1186 """ 

1187 # Initialize scope_to_highest_var_vers 

1188 con_scopestr = con_scope_to_str(node.con_scope) 

1189 # create VAR_INIT_VERSION of any modified or accessed variables 

1190 self.init_highest_var_vers_dict(con_scopestr, node.used_vars.keys()) 

1191 

1192 # visit children 

1193 self.visit_node_list(node.func_args, assign_lhs) 

1194 self.visit_node_list(node.body, assign_lhs) 

1195 

1196 # store highest var version 

1197 node.body_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1198 con_scopestr 

1199 ] 

1200 

1201 # DEBUG printing 

1202 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

1203 print(f"\nFor FUNCTION COPY: {con_scopestr}") 

1204 print(f" BodyHighestVers: {node.body_highest_var_vers}") 

1205 

1206 @_visit.register 

1207 def visit_function_def(self, node: AnnCastFunctionDef, assign_lhs: bool): 

1208 # Initialize scope_to_highest_var_vers 

1209 con_scopestr = con_scope_to_str(node.con_scope) 

1210 # create versions 0 of any modified or accessed variables 

1211 self.init_highest_var_vers_dict(con_scopestr, node.used_vars.keys()) 

1212 

1213 # visit children 

1214 self.visit_node_list(node.func_args, assign_lhs) 

1215 self.visit_node_list(node.body, assign_lhs) 

1216 

1217 # store highest var version 

1218 node.body_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1219 con_scopestr 

1220 ] 

1221 

1222 # populate FunctionDef nodes's top interface with arguments 

1223 # The pattern for the top interface is as follows: 

1224 # For each argument, we create a GrFN variable using the arguments index 

1225 # E.g. Arg0, Arg1, ... 

1226 # top interface inputs: Arg0, Arg1,... 

1227 # top interface outputs: NamedParam0, NamedParam1, ... 

1228 self.func_def_top_interface_args(node) 

1229 

1230 # add return value to bot interface out if functiondef has a ret_val 

1231 if node.has_ret_val: 

1232 self.func_def_ret_val_creation(node) 

1233 

1234 # add globals to functiondef integfaces 

1235 if is_func_def_main(node): 

1236 self.add_globals_to_main_func_def_interfaces(node) 

1237 else: 

1238 self.add_globals_to_non_main_func_def_interfaces(node) 

1239 

1240 # DEBUG printing 

1241 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

1242 print(f"\nFor FUNCTION: {con_scopestr}") 

1243 print(f" BodyHighestVers: {node.body_highest_var_vers}") 

1244 

1245 @_visit.register 

1246 def visit_literal_value(self, node: AnnCastLiteralValue, assign_side): 

1247 if node.value_type == "List[Any]": 

1248 # val has 

1249 # operator - string 

1250 # size - Var node or a LiteralValue node (for number) 

1251 # initial_value - LiteralValue node 

1252 val = node.value 

1253 

1254 # visit size's anncast name node 

1255 self.visit(val.size, assign_side) 

1256 

1257 # List literal doesn't need to add any other changes 

1258 # to the anncast at this pass 

1259 

1260 elif node.value_type == StructureType.TUPLE: # or node.value_type == StructureType.LIST: 

1261 self.visit_node_list(node.value, assign_side) 

1262 elif node.value_type == ScalarType.INTEGER: 

1263 pass 

1264 elif node.value_type == ScalarType.ABSTRACTFLOAT: 

1265 pass 

1266 pass 

1267 

1268 @_visit.register 

1269 def visit_loop(self, node: AnnCastLoop, assign_lhs: bool): 

1270 # Initialize scope_to_highest_var_version 

1271 if len(node.pre) > 0: 

1272 pre_scopestr = con_scope_to_str(node.con_scope + [LOOPPRE]) 

1273 expr_scopestr = con_scope_to_str(node.con_scope + [LOOPEXPR]) 

1274 body_scopestr = con_scope_to_str(node.con_scope + [LOOPBODY]) 

1275 if len(node.post) > 0: 

1276 post_scopestr = con_scope_to_str(node.con_scope + [LOOPPOST]) 

1277 

1278 # Initialize LoopInit 

1279 # create versions 0 of any modified or accessed variables 

1280 if len(node.pre) > 0: 

1281 self.init_highest_var_vers_dict( 

1282 pre_scopestr, node.used_vars.keys() 

1283 ) 

1284 

1285 # Initialize LoopExpr 

1286 # create versions 0 of any modified or accessed variables 

1287 self.init_highest_var_vers_dict(expr_scopestr, node.used_vars.keys()) 

1288 

1289 # Initialize LoopBody 

1290 # create versions 0 of any modified or accessed variables 

1291 self.init_highest_var_vers_dict(body_scopestr, node.used_vars.keys()) 

1292 

1293 # Initialize LoopPost 

1294 if len(node.post) > 0: 

1295 self.init_highest_var_vers_dict( 

1296 post_scopestr, node.used_vars.keys() 

1297 ) 

1298 

1299 ######## visit children ######## 

1300 if len(node.pre) > 0: 

1301 self.visit_node_list(node.pre, assign_lhs) 

1302 

1303 self.visit(node.expr, assign_lhs) 

1304 self.visit_node_list(node.body, assign_lhs) 

1305 

1306 if len(node.post) > 0: 

1307 self.visit_node_list(node.post, assign_lhs) 

1308 

1309 # print(node.used_vars) 

1310 

1311 # store highest var version 

1312 if len(node.pre) > 0: 

1313 node.pre_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1314 pre_scopestr 

1315 ] 

1316 

1317 node.expr_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1318 expr_scopestr 

1319 ] 

1320 node.body_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1321 body_scopestr 

1322 ] 

1323 

1324 if len(node.post) > 0: 

1325 node.post_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1326 post_scopestr 

1327 ] 

1328 

1329 # populate all of this loops interfaces 

1330 self.populate_loop_interfaces(node) 

1331 

1332 # DEBUG printing 

1333 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

1334 print(f"\nFor LOOP: {con_scope_to_str(node.con_scope)}") 

1335 if len(node.pre) > 0: 

1336 print(f" PreHighestVers: {node.pre_highest_var_vers}") 

1337 print(f" ExprHighestVers: {node.expr_highest_var_vers}") 

1338 print(f" BodyHighestVers: {node.body_highest_var_vers}") 

1339 if len(node.Post) > 0: 

1340 print(f" PostHighestVers: {node.post_highest_var_vers}") 

1341 

1342 @_visit.register 

1343 def visit_model_break(self, node: AnnCastModelBreak, assign_lhs: bool): 

1344 pass 

1345 

1346 @_visit.register 

1347 def visit_model_continue( 

1348 self, node: AnnCastModelContinue, assign_lhs: bool 

1349 ): 

1350 pass 

1351 

1352 @_visit.register 

1353 def visit_model_import(self, node: AnnCastModelImport, assign_lhs: bool): 

1354 pass 

1355 

1356 @_visit.register 

1357 def visit_model_if(self, node: AnnCastModelIf, assign_lhs: bool): 

1358 # Initialize scope_to_highest_var_version 

1359 expr_scopestr = con_scope_to_str(node.con_scope + [IFEXPR]) 

1360 ifbody_scopestr = con_scope_to_str(node.con_scope + [IFBODY]) 

1361 elsebody_scopestr = con_scope_to_str(node.con_scope + [ELSEBODY]) 

1362 # initialize IfExpr 

1363 # create versions 0 of any modified or accessed variables 

1364 self.init_highest_var_vers_dict(expr_scopestr, node.used_vars.keys()) 

1365 

1366 # initialize IfBody 

1367 # create versions 0 of any modified or accessed variables 

1368 self.init_highest_var_vers_dict(ifbody_scopestr, node.used_vars.keys()) 

1369 

1370 # initialize ElseBody 

1371 # create versions 0 of any modified or accessed variables 

1372 self.init_highest_var_vers_dict( 

1373 elsebody_scopestr, node.used_vars.keys() 

1374 ) 

1375 

1376 # visit children 

1377 self.visit(node.expr, assign_lhs) 

1378 self.visit_node_list(node.body, assign_lhs) 

1379 self.visit_node_list(node.orelse, assign_lhs) 

1380 

1381 # store highest var versions 

1382 node.expr_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1383 expr_scopestr 

1384 ] 

1385 node.ifbody_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1386 ifbody_scopestr 

1387 ] 

1388 node.elsebody_highest_var_vers = self.con_scope_to_highest_var_vers[ 

1389 elsebody_scopestr 

1390 ] 

1391 

1392 # populate interfaces 

1393 self.populate_model_if_interfaces(node) 

1394 

1395 # DEBUG printing 

1396 if self.pipeline_state.PRINT_DEBUGGING_INFO: 

1397 print(f"\nFor IF: {con_scope_to_str(node.con_scope)}") 

1398 print(f" ExprHighestVers: {node.expr_highest_var_vers}") 

1399 print(f" IfBodyHighestVers: {node.ifbody_highest_var_vers}") 

1400 print(f" ElseBodyHighestVers: {node.elsebody_highest_var_vers}") 

1401 

1402 @_visit.register 

1403 def visit_return(self, node: AnnCastModelReturn, assign_lhs: bool): 

1404 self.visit(node.value, assign_lhs) 

1405 

1406 @_visit.register 

1407 def visit_module(self, node: AnnCastModule, assign_lhs: bool): 

1408 con_scopestr = con_scope_to_str(node.con_scope) 

1409 # create VAR_INIT_VERSION of any modified or accessed variables 

1410 self.init_highest_var_vers_dict(con_scopestr, node.used_vars.keys()) 

1411 self.visit_node_list(node.body, assign_lhs) 

1412 

1413 @_visit.register 

1414 def visit_name(self, node: AnnCastName, assign_lhs: bool): 

1415 con_scopestr = con_scope_to_str(node.con_scope) 

1416 if assign_lhs: 

1417 self.incr_version_in_con_scope(con_scopestr, node.id, node.name) 

1418 

1419 node.version = self.get_highest_ver_in_con_scope(con_scopestr, node.id) 

1420 

1421 @_visit.register 

1422 def visit_operator(self, node: AnnCastOperator, assign_lhs: bool): 

1423 # visit operands 

1424 self.visit_node_list(node.operands, assign_lhs) 

1425 

1426 @_visit.register 

1427 def visit_set(self, node: AnnCastSet, assign_lhs: bool): 

1428 pass 

1429 

1430 @_visit.register 

1431 def visit_tuple(self, node: AnnCastTuple, assign_lhs: bool): 

1432 self.visit_node_list(node.values, assign_lhs) 

1433 

1434 @_visit.register 

1435 def visit_var(self, node: AnnCastVar, assign_lhs: bool): 

1436 self.visit(node.val, assign_lhs)