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
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
1import typing
2from functools import singledispatchmethod
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)
43class VariableVersionPass:
44 def __init__(self, pipeline_state: PipelineState):
45 self.pipeline_state = pipeline_state
46 self.nodes = self.pipeline_state.nodes
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 = {}
52 for node in self.pipeline_state.nodes:
53 # when visitor starts, assign_lhs is False
54 self.visit(node, False)
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 )
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]
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]
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`
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)
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)
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 )
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
132 The motivation for this is the difference between how the gcc and Python handle
133 variable declaration.
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.
139 The Python AST follows Python semantics, and variables are introduced inline dynamically.
140 This leads to many challenges creating interfaces e.g.
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)
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
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 )
183 func_def_node.dummy_grfn_assignments.append(dummy_assignment)
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)
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
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)
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
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)
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
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)
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)
318 # parameter name and scopestr
319 assert isinstance(param, AnnCastVar)
320 param_name = param.val.name
322 # argument and parameter share id, and start with initial version
323 id = param.val.id
324 version = VAR_INIT_VERSION
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)
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)
353 # link argument and parameter through top interface
354 node.top_interface_in[id] = arg_fullid
355 node.top_interface_out[id] = param_fullid
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}")
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
376 # interior container scope
377 func_scopestr = con_scope_to_str(node.con_scope)
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)
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)
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
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}")
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)
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 )
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)
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
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}")
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)
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
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
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)
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)
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}")
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`
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)
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)
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 )
621 # argument and parameter share id, and start with initial version
622 id = self.pipeline_state.next_collapsed_id()
623 version = VAR_INIT_VERSION
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)
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)
656 # link argument and parameter through top interface
657 node.top_interface_in[id] = arg_fullid
658 node.top_interface_out[id] = param_fullid
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}")
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`
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)
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)
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 )
691 # argument and parameter share id, and start with initial version
692 id = self.pipeline_state.next_collapsed_id()
693 version = VAR_INIT_VERSION
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)
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)
723 # link argument and parameter through top interface
724 node.top_interface_in[id] = arg_fullid
725 node.top_interface_out[id] = param_fullid
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}")
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
745 # interior container scope
746 call_con_scopestr = con_scope_to_str(
747 node.func.con_scope + [call_container_name(node)]
748 )
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)
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)
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
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`
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)
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)
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 )
803 # argument and parameter share id, and start with initial version
804 id = self.pipeline_state.next_collapsed_id()
805 version = VAR_INIT_VERSION
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)
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)
833 # link argument and parameter through top interface
834 node.top_interface_in[id] = arg_fullid
835 node.top_interface_out[id] = param_fullid
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}")
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
855 # interior container scope
856 call_con_scopestr = con_scope_to_str(
857 node.func.con_scope + [call_container_name(node)]
858 )
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)
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)
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
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
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)
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 )
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)
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 )
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
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}")
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)
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
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 )
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)
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)
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}")
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)
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)}")
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]
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)
1100 @_visit.register
1101 def visit_attribute(self, node: AnnCastAttribute, assign_lhs: bool):
1102 pass
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 )
1110 if node.is_grfn_2_2:
1111 self.visit_call_grfn_2_2(node, assign_lhs)
1112 return
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)
1136 # add return value to bot interface out
1137 if node.has_ret_val:
1138 self.populate_call_bot_interface_with_ret_val(node)
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)
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)
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)
1160 # add globals to call interface
1161 self.add_globals_to_grfn_2_2_call_interfaces(node)
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)
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
1176 @_visit.register
1177 def visit_label(self, node: AnnCastLabel, assign_lhs):
1178 # self.visit(node.label, at_module_scope, assign_lhs)
1179 pass
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())
1192 # visit children
1193 self.visit_node_list(node.func_args, assign_lhs)
1194 self.visit_node_list(node.body, assign_lhs)
1196 # store highest var version
1197 node.body_highest_var_vers = self.con_scope_to_highest_var_vers[
1198 con_scopestr
1199 ]
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}")
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())
1213 # visit children
1214 self.visit_node_list(node.func_args, assign_lhs)
1215 self.visit_node_list(node.body, assign_lhs)
1217 # store highest var version
1218 node.body_highest_var_vers = self.con_scope_to_highest_var_vers[
1219 con_scopestr
1220 ]
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)
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)
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)
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}")
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
1254 # visit size's anncast name node
1255 self.visit(val.size, assign_side)
1257 # List literal doesn't need to add any other changes
1258 # to the anncast at this pass
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
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])
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 )
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())
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())
1293 # Initialize LoopPost
1294 if len(node.post) > 0:
1295 self.init_highest_var_vers_dict(
1296 post_scopestr, node.used_vars.keys()
1297 )
1299 ######## visit children ########
1300 if len(node.pre) > 0:
1301 self.visit_node_list(node.pre, assign_lhs)
1303 self.visit(node.expr, assign_lhs)
1304 self.visit_node_list(node.body, assign_lhs)
1306 if len(node.post) > 0:
1307 self.visit_node_list(node.post, assign_lhs)
1309 # print(node.used_vars)
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 ]
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 ]
1324 if len(node.post) > 0:
1325 node.post_highest_var_vers = self.con_scope_to_highest_var_vers[
1326 post_scopestr
1327 ]
1329 # populate all of this loops interfaces
1330 self.populate_loop_interfaces(node)
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}")
1342 @_visit.register
1343 def visit_model_break(self, node: AnnCastModelBreak, assign_lhs: bool):
1344 pass
1346 @_visit.register
1347 def visit_model_continue(
1348 self, node: AnnCastModelContinue, assign_lhs: bool
1349 ):
1350 pass
1352 @_visit.register
1353 def visit_model_import(self, node: AnnCastModelImport, assign_lhs: bool):
1354 pass
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())
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())
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 )
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)
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 ]
1392 # populate interfaces
1393 self.populate_model_if_interfaces(node)
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}")
1402 @_visit.register
1403 def visit_return(self, node: AnnCastModelReturn, assign_lhs: bool):
1404 self.visit(node.value, assign_lhs)
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)
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)
1419 node.version = self.get_highest_ver_in_con_scope(con_scopestr, node.id)
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)
1426 @_visit.register
1427 def visit_set(self, node: AnnCastSet, assign_lhs: bool):
1428 pass
1430 @_visit.register
1431 def visit_tuple(self, node: AnnCastTuple, assign_lhs: bool):
1432 self.visit_node_list(node.values, assign_lhs)
1434 @_visit.register
1435 def visit_var(self, node: AnnCastVar, assign_lhs: bool):
1436 self.visit(node.val, assign_lhs)