def search_symbol_for_value(self, symbol: Symbol, name: list, type=SYMBOL_VAR): """ Traverses through symbol values to try and find a value """ # One at a time, loops through each component of the name while len(name) > 0: # If the current symbol is of type INSTANCE (Storing another scope) if symbol.data_type == DATA_INSTANCE: symbol = self.search_scope_for_symbol(symbol.value, name[0], type) # If current scope is of type LIST elif symbol.data_type == DATA_LIST: try: symbol = symbol.value[int(name[0])] except: raise InvalidSymbolAccess( "Invalid list access `" + name[0] + "` for symbol value `" + str(symbol.value) + "`. Scriptax.SymbolTable@search_symbol_for_value") if not isinstance(symbol, Symbol): symbol = Symbol(name=name[0], value=symbol) # If current scope is of type DICT elif symbol.data_type == DATA_DICT: try: symbol = symbol.value[str(name[0])] except: raise InvalidSymbolAccess( "Invalid dict access `" + name[0] + "` for symbol value `" + str(symbol.value) + "`. Scriptax.SymbolTable@search_symbol_for_value") if not isinstance(symbol, Symbol): symbol = Symbol(name=name[0], value=symbol) # If current scope is of type PYTHONIC elif symbol.data_type == DATA_PYTHONIC: symbol = Symbol(name=name[0], value=None) # If current scope is of type THREAD elif symbol.data_type == DATA_THREAD: symbol = Symbol(name=name[0], value=None) else: raise InvalidSymbolAccess( "Unsupported symbol access `" + name[0] + "` for symbol value `" + str(symbol.value) + "`. Scriptax.SymbolTable@search_symbol_for_value") name.pop(0) return symbol.value
def remove_symbol(self, name: str, type=SYMBOL_VAR): """ Remove a symbol """ if not self.has_symbol(name, type): raise InvalidSymbolAccess( "Cannot remove symbol with name `" + name + "` as it does not exist within this scope. Scriptax.SymbolTable@remove_symbol" ) name: list = self.make_and_verify_name(name) scope: SymbolScope = self.traverse_up(name) # See if a symbol already exists, if not, it excepts try: symbol: Symbol = self.search_scope_for_symbol(scope, name[0], type) except: raise InvalidSymbolAccess( "Cannot remove symbol with name `" + "".join(name) + "` as it does not exist within this scope. Scriptax.SymbolTable@remove_symbol" ) # If a symbol exists, this section will remove it try: # Pops the name we used to find the starting symbol from the first try/except above name.pop(0) # If the name still has more components, then we haven't yet found the right symbol to remove if len(name) > 0: # Index holds the last component in the name index = name[-1] # Finds the symbol corresponding to the name excluding the last name component symbol = Symbol(name="".join(name), value=self.search_symbol_for_value( symbol, name[:-1], type)) # If the symbol is an INSTANCE (Another scope), then find the symbol in that scope and remove it if symbol.data_type == DATA_INSTANCE: symbol.value.remove_symbol(name=index) # If the symbol is a DICT, remove it the key elif symbol.data_type == DATA_DICT: symbol.value.pop(str(index)) # If the symbol is a LIST, remove the index elif symbol.data_type == DATA_LIST: symbol.value.pop(int(index)) # If the symbol is any other type, then we should not be able to apply another name component to it, # thus it is an error else: raise InvalidSymbolAccess( "Unsupported symbol access `" + index + "` for symbol value `" + str(symbol.value) + "`. Scriptax.SymbolTable@remove_symbol") else: # If the name has no more components, we have already found the symbol we want to remove scope.remove_symbol(symbol=symbol) except: raise InvalidSymbolAccess( "Removing existing symbol failed. Scriptax.SymbolTable@remove_symbol" )
def set_symbol(self, name: str, value, type=SYMBOL_VAR): """ Set's the value of an existing symbol, creates the symbol if it does not exist yet, handles LIST and DICT as well """ name: list = self.make_and_verify_name(name) scope: SymbolScope = self.traverse_up(name) # See if a symbol already exists, if not, it excepts and will insert a new symbol try: symbol: Symbol = self.search_scope_for_symbol(scope, name[0], type) except: # If we are inserting a new symbol, then the symbol name must only have a single component if len(name) > 1: raise InvalidSymbolAccess( "Variable access contains too many components for uninitialized subcomponent `" + "".join(name) + "`. Scriptax.SymbolTable@set_symbol") scope.insert_symbol(Symbol(name=name[0], value=value)) return # If a symbol already exists, this section will modify its value try: # Pops the name we used to find the starting symbol from the first try/except above name.pop(0) # If the name still has more components, then we haven't yet found the right value to modify if len(name) > 0: # Index holds the last component in the name index = name[-1] # Finds the symbol corresponding to the name excluding the last name component symbol = Symbol(name="".join(name), value=self.search_symbol_for_value( symbol, name[:-1], type)) # If the symbol is an INSTANCE (Another scope), then find the symbol in that scope and modify its value # This will not insert a brand new symbol into the scope, thus all symbols MUST be pre-defined in the # scope if symbol.data_type == DATA_INSTANCE: symbol = symbol.value.get_symbol(index) symbol.value = value # If the symbol is a DICT, add it via dictionary indexing elif symbol.data_type == DATA_DICT: symbol.value[str(index)] = value # If the symbol is a LIST, add it via numeric index elif symbol.data_type == DATA_DICT: symbol.value[int(index)] = value # If the symbol is any other type, then we should not be able to apply another name component to it, # thus it is an error else: raise InvalidSymbolAccess( "Unsupported symbol access `" + index + "` for symbol value `" + str(symbol.value) + "`. Scriptax.SymbolTable@set_symbol") else: # If the name has no more components, we have already found the symbol we want to modify symbol.value = value except: raise InvalidSymbolAccess( "Altering existing symbol failed. Scriptax.SymbolTable@set_symbol" )
def import_module(self, name: str, module: Import) -> Symbol: """ Imports a new module to the symbol table This does not handle the parse tree or the static method scoping and that should be done prior to calling this :param name: :param module: :return: """ return self.scope().insert_symbol( Symbol(name=name, symbol_type=SYMBOL_MODULE, value=module))
def register_method(self, name: str, method_context, attributes: Attributes) -> Symbol: """ Registers a method to the symbol table """ symbol: Symbol = Symbol(name=name, data_type=DATA_METHOD, value=method_context, attributes=attributes.dict()) self.scope().insert_symbol(symbol) return symbol
def insert_symbol(self, symbol: Symbol) -> Symbol: """ Inserts a Symbol into this SymbolScope If a Symbol already exists with the same name, throws exception """ symbol.name = SymbolScope.conform_name(symbol.name) if self.has_symbol(symbol.name, symbol.symbol_type): raise Exception( 'SymbolScope already contains symbol `' + symbol.name + '` and cannot insert a new one. Scriptax.SymbolScope@insert_symbol' ) self.symbols.append(symbol) return symbol
def execute_method(self, label, parameters: List[Parameter]) -> BlockStatus: """ Execute a method found inside of the current scope or any of its static parents going up the interface and inheritance tree """ label = self.resolve_label(label) method_context: AhParser.Method_def_atomContext = self.symbol_table.execute( name=label, parameters=parameters) if not method_context: self.symbol_table.complete_execution() raise InvalidSymbolAccess( "Cannot execute something which is not a method " + label + ". Scriptax.Visitor@execute_method") # Add in optional parameters and verify that the two parameter lists (defined & passed) match parameter_names = [] for parameter in parameters: parameter_names.append(parameter.name) defined_parameters = self.visit( method_context.flexible_parameter_block()) defined_parameter_names = [] # Check defined parameters against passed in parameters and add in optional ones for parameter in defined_parameters: if parameter.required and parameter.name not in parameter_names: raise InvalidParameters( "Required parameter " + parameter.name + " not found in parameter list. Scriptax.Visitor@execute_method" ) if not parameter.required and parameter.name not in parameter_names: self.symbol_table.current.insert_symbol( Symbol(name=parameter.name, value=parameter.value)) defined_parameter_names.append(parameter.name) # Check passed in parameters to ensure there aren't extra ones which were not defined for parameter in parameters: if parameter.name not in defined_parameter_names: raise InvalidParameters("Unexpected parameter " + parameter.name + ". Scriptax.Visitor@execute_method") # TODO: If async attribute is true, then this needs to be launched in a thread result = self.visit(method_context.block()) self.symbol_table.complete_execution() return result
def execute(self, name: str, parameters: List[Parameter] = None, isolated_scope: bool = False): """ Executes a method by getting the method body, and its appropriate static parent and birthing a scope for the method to operate within Returns the method body """ if self.has_symbol(name, SYMBOL_MODULE): module_import: Import = self.get_symbol(name, type=SYMBOL_MODULE, as_value=False).value scope = module_import.scope method_name = self.make_and_verify_name(name)[1] tbl = create_table(scope) method: Symbol = tbl.get_symbol(name=method_name, as_value=False) instance_table: GenericTable = create_generic_table(scope) method_scope: SymbolScope = instance_table.birth_scope( name=method_name + "_method") # Adds each parameter into the method scope if parameters: for parameter in parameters: method_scope.insert_symbol( Symbol(name=parameter.name, value=parameter.value)) # Sets up the dynamic links from the currently executing code to the called method self.call(method_scope) # Returns the body method such that some parser or compiler can execute its body return method.value # Ensures that the method symbol exists if not self.has_symbol(name) and self.has_symbol(name, SYMBOL_MODULE): raise SymbolNotFound( "Cannot execute `" + name + "` as it does not exist within scope. Scriptax.SymbolTable@execute" ) # Breaks apart the name into the scope name and the method name # The scope name is everything before the method name name = self.make_and_verify_name(name) # Get the scope accessing name which is everything but the last component of the name scope_name = name[:-1] # If scope_name only contains an up_word, then clear out the scope_name entirely if len(scope_name) == 1 and self.name_has_up(scope_name): scope_name = [] # Get the Method name which is just the last component of the name method_name = name[-1] # print("mn:" + str(method_name)) # print("sn:" + str(scope_name)) # These two lines are not needed, but are left in for readabilities sake instance_scope: SymbolScope = None # This is the scope where the method is defined method_scope: SymbolScope = None # This is the scope which will be used to execute the method # If there is a scope_name, we must be operating on an instance # If there is no scope_name, then we must be trying to call a function on our own instance # or calling a method stored in a var if len(scope_name) > 0: # Attempts to find the symbol on instances try: instance_scope = self.get_symbol(name=".".join(scope_name)) except: # If we cant find an instance with the name, then try and see if we have imported this name if not self.name_has_up(scope_name): scope_name.insert(0, self.up_words[0]) instance_scope = self.get_symbol(name=".".join(scope_name), type=SYMBOL_MODULE) else: # Perhaps we are trying to call a function stored in a variable instance_scope = self.current # Let's see whether the instance_scope contains the method tbl = create_table(instance_scope) try: method: Symbol = tbl.get_symbol(name=method_name, as_value=False) except: # If one doesnt exist, then we must be trying to call a function on our own instance instance_scope = self._get_parent_module() # Gets the method symbol from the scope tbl = create_table(instance_scope) try: method: Symbol = tbl.get_symbol(name=method_name, as_value=False) except: # print(instance_scope.scope_parent.scope_children[1].symbols[0].name) # try: # while tbl.current.scope_parent and tbl.current.scope_parent.scope_children[1] != tbl.current: # print("WHAT") # print(tbl.current.scope_parent.scope_children[1].name) # tbl = create_table(tbl.current.scope_parent.scope_children[1]) # print(tbl.current.symbols[0].name) # print(method_name) # print(tbl.current.print_scope_debug()) # method: Symbol = tbl.get_symbol(name=method_name, as_value=False) # except Exception: raise SymbolNotFound( "Method `" + ".".join(name) + "` does not exist. Scriptax.SymbolTable@execute") # Used in a weird edge case where we pass a default parameter which is a method atom # TODO: Hopefully in Scriptax 5 this will no longer be required while isinstance(method.value, Symbol) and method.value.data_type == DATA_METHOD: method = method.value if method.data_type != DATA_METHOD: raise InvalidSymbolAccess( "Cannot execute non executable data type `" + method.data_type + "`. Scriptax.SymbolTable@execute") # Spawns an anonymous scope if this should be executed in isolation, otherwise use the existing # static scope if isolated_scope: method_scope = SymbolTable(name=method_name + "_isolated_method", type=SCOPE_BLOCK).scope() else: # Births a new scope for the method body inside of its static parent scope instance_table: GenericTable = create_generic_table(instance_scope) method_scope: SymbolScope = instance_table.birth_scope( name=method_name + "_method") # Adds each parameter into the method scope if parameters: for parameter in parameters: method_scope.insert_symbol( Symbol(name=parameter.name, value=parameter.value)) # Sets up the dynamic links from the currently executing code to the called method self.call(method_scope) # Returns the body method such that some parser or compiler can execute its body return method.value
# symbol_table.printTable() from scriptax.parser.symbols.ARISymbolTable import ARISymbolTable, create_table from scriptax.parser.symbols.SymbolScope import SymbolScope, SCOPE_GLOBAL, SCOPE_MODULE from scriptax.parser.symbols.Symbol import Symbol, DATA_METHOD # Simulation: # Starting program table = ARISymbolTable(name="program", type=SCOPE_GLOBAL) # Loading the .ah file as a module table.enter_scope(name='main', type=SCOPE_MODULE) # During the parsing of the .ah file add all of the methods as symbols including the constructor as so: table.scope().insert_symbol(Symbol(name="construct", data_type=DATA_METHOD)) # While parsing .ah file, an instance variable declaration was found instance_table = ARISymbolTable(name="myInstance") instance_table.scope().insert_symbol( Symbol(name="getValue", data_type=DATA_METHOD)) table.scope().insert_symbol( Symbol(name="myInstance", value=instance_table.scope())) # After parsing the .ah file, execute the constructor by entering an anonymous scope table.enter_scope(name="constructor_block") # Below is doing the constructor method body # In the constructor, we execute a call to the getValue method on our myInstance variable instance_scope: SymbolScope = table.scope().scope_parent.get_symbol(