def processMacro(self, element: Element, *args: typing.Any) -> str: """ Process a macro already defined. """ sequence = element.getNestedSequence("nested") assert sequence # Build the argument list argList = [] argument = element.getNestedSequence("argument") assert argument is not None for arg in argument: Error.assertHasAttr(element=arg, attr="name") argList.append(arg.getAttr("name").value) # Sanity check Error.assertTrue( element=element, condition=len(argList) == len(args), message="Wrong number of argument(s), expected {}: {}".format( len(argList), ", ".join(argList))) for i, name in enumerate(argList): self.substitutions.register(element=element, key=name, value=args[i]) # Process the template result = self._visit(sequence=sequence) for name in argList: self.substitutions.unregister(name) return "".join(result)
def visitSubstitution(self, element: Element, result: ResultType) -> None: """ Handle substitution. """ Error.assertHasAttr(element=element, attr="name") name = element.getAttr("name").value # Check if callable and if there are arguments arguments = element.getNestedSequence("argument") if arguments is None: value = self.resolveName(name=name) else: # Resolve all arguments args = [] for arg in arguments: args.append(self.readValue(element=arg)) value = self.resolveName(name, True, *args) # Process the pipes if any. pipes = element.getNestedSequence("pipe") if pipes: for pipe in pipes: Error.assertHasAttr(element=pipe, attr="name") value = self.resolveName( pipe.getAttr("name").value, True, value) # Save the output assert isinstance( value, (int, float, str, pathlib.Path) ), "The resulting substitued value must be a number, a string or a path." self.appendSubstitution(element=element, result=result, string=str(value))
def elementToEntity(element: Element, extension: typing.Optional[typing.Dict[str, typing.Type[EntityType]]] = None) -> EntityType: """ Instantiate an entity from an element. """ Error.assertHasAttr(element=element, attr="category") category = element.getAttr("category").value if extension and category in extension: return extension[category](element=element) if category not in CATEGORY_TO_ENTITY: Error.handleFromElement(element=element, message="Unexpected element category: {}".format(category)) return CATEGORY_TO_ENTITY[category](element=element)
def __init__(self, element: Element, kind: str, underlyingType: typing.Optional[str] = None, template: typing.Optional[str] = None, argumentTemplate: typing.Optional[str] = None, contract: typing.Optional[str] = None, const: typing.Optional[str] = None) -> None: Error.assertHasAttr(element=element, attr=kind) self.element = element self.kindAttr = kind self.underlyingTypeAttr = underlyingType self.templateAttr = template self.argumentTemplateAttr = argumentTemplate self.contractAttr = contract self.constAttr = const
def visitForBlock(self, element: Element) -> ResultType: """ Handle for loop block. """ Error.assertHasAttr(element=element, attr="value1") Error.assertHasAttr(element=element, attr="iterable") Error.assertHasSequence(element=element, sequence="nested") value1 = element.getAttr("value1").value value2 = element.getAttrValue("value2") sequence = element.getNestedSequence(kind="nested") assert sequence block: ResultType = [] # Loop through the elements iterable = self.resolveName(name=element.getAttr("iterable").value) if value2 is None: for value in iterable: self.substitutions.register(element=element, key=value1, value=value) block += self._visit(sequence=sequence) self.substitutions.unregister(value1) else: iterablePair = iterable.items() if isinstance( iterable, dict) else enumerate(iterable) for key, value in iterablePair: self.substitutions.register(element=element, key=value1, value=key) self.substitutions.register(element=element, key=value2, value=value) block += self._visit(sequence=sequence) self.substitutions.unregister(value2) self.substitutions.unregister(value1) return block
def readValue(self, element: Element) -> typing.Any: """ Read a value from an element. """ Error.assertHasAttr(element=element, attr="value") Error.assertHasAttr(element=element, attr="type") valueType = element.getAttr("type").value value = element.getAttr("value").value if valueType == "name": return self.resolveName(value) elif valueType == "number": return float(value) elif valueType == "string": return value elif valueType == "true": return True elif valueType == "false": return False Error.handleFromElement(element=element, message="Unsupported value type.")
def visitMacro(self, element: Element) -> None: """ Handle macro definition block. """ Error.assertHasSequence(element=element, sequence="argument") Error.assertHasSequence(element=element, sequence="nested") Error.assertHasAttr(element=element, attr="name") name = element.getAttr("name").value Error.assertTrue( element=element, attr="name", condition=(name not in self.substitutions), message= "Name conflict with macro and an already existing name: '{}'.". format(name)) # Register the macro self.substitutions.register( element=element, key=name, value=lambda *args: self.processMacro(element, *args))
def visitElement(self, element: Element, result: typing.List[str], nested: typing.Optional[typing.List[str]]) -> typing.List[str]: if element.isAttr("type"): entity = Type(element=element, kind="type", underlyingType="fqn_type", template="template_resolved" if self.isResolved else "template", argumentTemplate="argument_template_resolved" if self.isResolved else None, const="const") output = self.visitType(entity=entity, nested=[] if nested is None else nested, parameters=entity.parametersTemplateResolved) else: Error.assertHasAttr(element=element, attr="value") Error.assertTrue(element=element, condition=not nested, message="Value cannot have nested entities.") output = self.visitValue(value=element.getAttr("value").value, comment=element.getAttrValue("comment")) result.append(output) return result
def __init__(self, element: Element) -> None: super().__init__(element, Role.Type) Error.assertHasAttr(element=element, attr="name")
def visitElement(self, element: Element, result: ResultType) -> ResultType: """ Go through all elements and dispatch the action. """ Error.assertHasAttr(element=element, attr="category") category = element.getAttr("category").value try: # Raw content if category == "content": Error.assertHasAttr(element=element, attr="content") string = element.getAttr("content").value result.append(string) # Substitution elif category == "substitution": self.visitSubstitution(element=element, result=result) # Comments elif category == "comment": pass # End statement elif category == "end": # Reset the else flag here to make sure nested if without else do not trigger. self.followElse = False # For loop block elif category == "for": block = self.visitForBlock(element=element) self.appendBlock(element=element, result=result, block=block) # If block elif category == "if": Error.assertHasAttr(element=element, attr="condition") conditionStr = element.getAttr("condition").value block = self.visitIfBlock(element=element, conditionStr=conditionStr) self.appendBlock(element=element, result=result, block=block) # Else block elif category == "else": block = [] if self.followElse: conditionStr = element.getAttrValue("condition", "True") # type: ignore block = self.visitIfBlock(element=element, conditionStr=conditionStr) self.appendBlock(element=element, result=result, block=block) # Macro block elif category == "macro": self.visitMacro(element=element) elif category == "include": block = self.visitInclude(element=element) self.appendBlock(element=element, result=result, block=block) else: raise Exception("Unsupported category: '{}'.".format(category)) # Propagate processed exception to the top layer except ExceptionParser as e: raise e except Exception as e: Error.handleFromElement(element=element, message=str(e)) return result
def __init__(self, element: Element) -> None: super().__init__(element, Role.Type) Error.assertHasAttr(element=element, attr="name") Error.assertHasSequence(element=element, sequence="values")
def visitElement(self, element: Element, result: typing.List[str]) -> typing.List[str]: Error.assertHasAttr(element=element, attr="name") result.append(element.getAttr("name").value) return result
def __init__(self, element: Element) -> None: super().__init__(element, Role.Meta) Error.assertHasAttr(element=element, attr="value")
def __init__(self, element: Element) -> None: Error.assertHasAttr(element=element, attr="type") self.element = element
def inheritanceList(self) -> typing.List[Type]: inheritanceList: typing.List[Type] = [] for element in self.element.getNestedSequenceOrEmpty("inheritance"): Error.assertHasAttr(element=element, attr="symbol") inheritanceList.append(Type(element=element, kind="symbol")) return inheritanceList
def __init__(self, element: Element) -> None: super().__init__(element, Role.Type) Error.assertHasAttr(element=element, attr="type") self.assertTrue(condition=self.type in TYPES, message="Unsupported nested type: '{}'.".format(self.type))