def insert(self, name: typing.Optional[str], namespace: typing.List[str], path: typing.Optional[Path], element: Element, category: str, conflicts: bool = False) -> str: """ Insert a new element into the symbol map. Args: fqn: Full qualified name. path: Path associated with the element. element: Element to be registered. category: Category associated with the element, will be used for filtering. conflicts: Handle symbol FQN conflicts. """ if name is None: # These are the unnamed elements that should be progpagated to other translation units. if category in {CATEGORY_COMPOSITION, CATEGORY_GLOBAL_COMPOSITION}: fqn = FQN.makeUnique(namespace=namespace) # If not, they will be kept private. else: fqn = FQN.makeUniquePrivate() else: # Build the symbol name. fqn = FQN.fromNamespace(name=name, namespace=namespace) assert fqn is not None # Save the FQN to the element, so that it can be found during [de]serialization. # This is also used to refer to the element with the symbol tree, the top-level ones only. # In addition, it is assumed that during while resolved any element have a valid FQN. ElementBuilder.cast(element, ElementBuilder).setAttr("fqn", fqn) if self.contains(fqn=fqn): originalElement = self.getEntityResolved(fqn=fqn).assertValue( element=element).element if not conflicts or element != originalElement: SymbolMap.errorSymbolConflict_(element, originalElement) self.map[fqn] = { "c": category, "p": path.as_posix() if path is not None else "", "e": None } # Resolve context context, _, _ = element.context.resolve() element.context = context # The element is also added to the entity map, to allow further modification that will # be written when serialize is called. self.entities[fqn] = elementToEntity(element=element) return fqn
def resolveFQN(self, name: str) -> resolveFQNResult: """ Find the fully qualified name of a given a name and a namespace. Note, name can be a partial fqn. """ maybeFQN = self.resolveShallowFQN(name=name) if not maybeFQN: return resolveFQNResult.makeError(maybeFQN.error) fqn: typing.Optional[str] fqn, unresolvedFQN = maybeFQN.value # List of matching FQNs fqns: typing.List[str] = [fqn] # Re-iterate the process with the next items for nextName in unresolvedFQN: entity = self.getEntityResolved(fqn=fqn).value potentialNamespaceFQNs = [fqn] # If it has an underlying type, add it to the list as well as its parents (if any). if entity.underlyingType is not None: potentialNamespaceFQNs += [ entity.underlyingType ] + entity.getEntityUnderlyingTypeResolved( resolver=self).getParents() # Check if there is any match. fqn = None for potentialNamespaceFQN in potentialNamespaceFQNs: potentialFQN = FQN.fromNamespace( name=nextName, namespace=FQN.toNamespace(potentialNamespaceFQN)) if self.symbols.contains(fqn=potentialFQN, exclude=self.exclude): fqn = potentialFQN break if fqn is None: return resolveFQNResult.makeError( "Symbol '{}' from '{}' in namespace '{}' could not be resolved." .format(nextName, name, ".".join(self.namespace) ) if self.namespace else "Symbol '{}' from '{}' could not be resolved.". format(nextName, name)) fqns.append(fqn) # Return the final FQN. return resolveFQNResult(fqns)
def visitType(self, entity: Type, nested: typing.List[str], parameters: ResolvedParameters) -> str: """ Called when an element needs to be formatted. """ # Add arguments template to the nested mix. nested += self.declareParametersResolvedValues(parameters) outputList: typing.List[str] = [] output: str for index, fqn in enumerate(entity.kinds): if fqn in knownTypes: if callable(knownTypes[fqn].transform): output, nested = knownTypes[fqn].transform( entity, nested, self.isReference) else: output = knownTypes[fqn].transform else: namespace = FQN.toNamespace(fqn) if index == 0: if self.isNamespaceToFQN: assert self.namespaceToFQN is not None output = self.namespaceToFQN(namespace) else: output = "::".join(namespace) else: output = namespace[-1] outputList.append(output) output = ".".join(outputList) # Apply the nested template if any. if nested: output += "<{}>".format(", ".join(nested)) if not self.isTopLevel: # TODO: support if there is no value. if entity.parametersResolved: output += "{{{}}}".format(", ".join([ expression.value for expression in entity.parametersResolved ])) else: if self.definition: if entity.underlyingType in knownTypes and knownTypes[ entity.underlyingType].constexpr: output = "constexpr " + output # Apply the reference if any. if self.isReference: output += "&" # Apply const if needed. if entity.const and (not self.isTopLevel or not self.nonConst): output = "const " + output return output
def resolveShallowFQN(self, name: str) -> ResolveShallowFQNResult: """ Find the fully qualified name of a given a name and a namespace. Note, name can be a partial fqn. """ nameFirst = FQN.toNamespace(name)[0] if nameFirst == "this": if self.this is None: return ResolveShallowFQNResult.makeError( "Keyword 'this' must be used in an object context.") nameFirst = self.this # Look for a symbol match of the first part of the name. potentialNamespace = self.namespace.copy() while True: fqn = FQN.fromNamespace(name=nameFirst, namespace=potentialNamespace) if self.symbols.contains(fqn=fqn, exclude=self.exclude): break if not potentialNamespace: return ResolveShallowFQNResult.makeError( "Symbol '{}' in namespace '{}' could not be resolved.". format(nameFirst, ".".join(self.namespace)) if self. namespace else "Symbol '{}' could not be resolved.". format(nameFirst)) potentialNamespace.pop() # Attempt to resolve as much as possible. remainingNamespace = FQN.toNamespace(name)[1:] while remainingNamespace: potentialFQN = FQN.fromNamespace(name=remainingNamespace[0], namespace=FQN.toNamespace(fqn)) if not self.symbols.contains(fqn=potentialFQN, exclude=self.exclude): break remainingNamespace.pop(0) fqn = potentialFQN # Return the FQN and the unresolved rest. return ResolveShallowFQNResult((fqn, remainingNamespace))
def getEntity(self, fqn: str, exclude: typing.Optional[typing.List[str]] = None ) -> Result[EntityType]: """ Return an element from the symbol map and resolves underlying types if needed. """ namespace = [] result = None for name in FQN.toNamespace(fqn): namespace.append(name) result = self.getEntityResolved( fqn=FQN.fromNamespace(namespace=namespace), exclude=exclude) if not result: continue if result.value.underlyingType is not None: namespace = FQN.toNamespace(result.value.underlyingType) if not result: return Result.makeError( "Could not resolve symbol '{}'.".format(fqn)) return Result(result.value)
def update(self, symbols: "SymbolMap") -> None: """ Register multiple symbols. """ for fqn, element in symbols.map.items(): # Ignore private entries if FQN.isPrivate(fqn): continue existingElement = self._get(fqn=fqn) if existingElement is not None and element["p"] != existingElement[ "p"]: SymbolMap.errorSymbolConflict_( SymbolMap.metaToElement(element), SymbolMap.metaToElement(existingElement)) self.map[fqn] = element
def fqnToAdapterStr(fqn: str) -> str: split = FQN.toNamespace(fqn) split.insert(-1, "adapter") return "::".join(split)
def fqnToStr(fqn: str) -> str: return "::".join(FQN.toNamespace(fqn))
def fqnToNameStr(fqn: str) -> str: split = FQN.toNamespace(fqn) return "_".join(split)
def close(self) -> None: """ Close the map to prevent any further editing. """ # Create serialized blobs for elements present in the entities map. removeFQNs: typing.Set[str] = set() for fqn, entity in self.entities.items(): # Ignore builtins if fqn in self.builtins: continue entity.assertTrue( condition=fqn in self.map, message= "Entry '{}' was not properly created before being added to the entities map." .format(fqn)) element = entity.element # Remove nested element and change them to references. if any([ element.isNestedSequence(category) for category in CATEGORIES ]): preparedElement = element.copy(ignoreNested=CATEGORIES) for category in CATEGORIES: nested = element.getNestedSequence(category) if nested is not None: sequence = SequenceBuilder() for nestedElement in nested: if nestedElement.isAttr("fqn"): fqnNested = nestedElement.getAttr("fqn").value # Remove private FQNs and keep them nested. # This is needed because when merging nested structure, some config # for example have unamed elements and need to be copied with the rest. if FQN.isPrivate(fqnNested): removeFQNs.add(fqnNested) ElementBuilder.cast( nestedElement, ElementBuilder).removeAttr("fqn") sequence.pushBackElement(nestedElement) else: sequence.pushBackElement( self.makeReference(fqnNested)) else: sequence.pushBackElement(nestedElement) if sequence: preparedElement.setNestedSequence( category, sequence) element = preparedElement # Serialize the element to the map. self.map[fqn]["e"] = element.serialize() # Remove some of the FQNs that have been copied. for fqn in removeFQNs: del self.map[fqn] # Sanity check to ensure that all entries in the map are valid. for fqn, entry in self.map.items(): assert "e" in entry and entry[ "e"], "Invalid element in the map: {}.".format(entry) assert "c" in entry and entry[ "c"], "Invalid category in the map: {}.".format(entry) assert "p" in entry, "Missing path in the map: {}.".format(entry) # Mark as closed. self.isClosed = True
def makeFQN(self, name: str) -> str: """ Create an FQN out of a name. """ return FQN.fromNamespace(name=name, namespace=self.namespace)
def namespaceToFQN(namespace: typing.List[str]) -> str: fqn = FQN.fromNamespace(namespace=namespace) assert registry is not None if fqn in registry: return "registry.{}_".format(fqnToNameStr(fqn)) return "::".join(namespace)
def namespace(self) -> typing.List[str]: return FQN.toNamespace(self.fqn)[:-1]