"""Frame
This file contains the frame component of the UPPAAL model.
This file can be imported and contains the following classes:
* Frame: Holds all variable and function bindings and represents a scope.
"""
from __future__ import annotations
import copy
import logging
from typing import Dict, Optional
from uppaal2jetracer.declarations.declarations_ast import FuncDef
from uppaal2jetracer.uppaalmodel.variable import Variable
from uppaal2jetracer.uppaalmodel.elements import Update
logger = logging.getLogger('uppaal_model')
[docs]
class Frame:
"""
Represents a visibility scope for variables and functions in the system.
:ivar _parent: The parent Frame.
:vartype _parent: Frame
"""
__slots__ = ('_functions', '_bindings', '_parent')
def __init__(self, parent: Frame = None):
self._parent = parent
self._functions: Dict[str, FuncDef] = {}
self._bindings: Dict[str, Variable] = {}
@property
def parent(self) -> Frame:
"""
The parent of the frame.
:return: The parent of the frame.
:rtype: Frame
"""
return self._parent
@parent.setter
def parent(self, frame: Frame):
self._parent = frame
[docs]
def add_function(self, name: str, n: FuncDef):
"""
Add function root node to the frame.
:param name: The reference name of the function.
:type name: str
:param n: The root node of the function
:type n: FuncDef
"""
logger.debug("Add function '%s' to frame!", name)
self._functions[name] = n
[docs]
def add_binding(self, name: str, v: Variable):
"""
Add variable reference to the frame.
:param name: The reference name of the variable.
:type name: str
:param v: The variable reference.
:type v: Variable
"""
logger.debug("Add Variable '%s' to frame!", name)
self._bindings[name] = v
[docs]
def get_function(self, name: str) -> Optional[FuncDef]:
"""
Return the function root node if name is found otherwise check parent frame.
:param name: Reference name to search for.
:type name: str
:return: Function root node if found, else return None.
:rtype: Optional[Node]
"""
logger.debug("Fetching function '%s' from frame!", name)
if self._functions.keys().__contains__(name):
return self._functions[name]
if self._parent is None:
return None
function = self._parent.get_function(name)
if function is None:
logger.debug("No function '%s' found!", name)
return function
[docs]
def get_variable(self, name: str) -> Optional[Variable]:
"""
Return the variable reference if name is found otherwise check parent frame.
:param name: Reference name to search for.
:type name: str
:return: Variable reference if found, else return None.
:rtype: Optional[Variable]
"""
logger.debug("Fetching variable '%s' from frame!", name)
if self._bindings.keys().__contains__(name):
return self._bindings[name]
if self._parent is None:
return None
variable = self._parent.get_variable(name)
if variable is None:
logger.debug("No variable '%s' found!", name)
return variable
[docs]
def create_temporary_frame(self, update: Update) -> Frame:
"""
Creates a child frame of this frame, having a copy of all the values assigned in the update.
:param update: The assignments the frame needs to contain.
:return: The new frame.
"""
new_frame = Frame(self)
logger.debug("Created temporary frame!")
if update is None:
return new_frame
for assignment in update.assignments:
name = assignment.children()[0][1].name
var = self.get_variable(name)
new_frame.add_binding(name, copy.deepcopy(var))
logger.debug("Added '%s' to temporary frame!", name)
return new_frame
[docs]
def reset(self):
"""
Resets all variables inside the frame to their starting value.
"""
logger.debug("Start to reset all variables in frame!")
for variable in self._bindings.values():
variable.reset()