Exemplo n.º 1
0
	def toResolvedSequence(self, resolver: "Resolver", varArgs: bool, onlyTypes: bool = False,
		onlyValues: bool = False) -> Sequence:
		"""
		Build the resolved sequence of those parameters.
		Must be called after mergeDefaults.
		"""
		sequence = SequenceBuilder()

		entries = self.itemsValuesOrTypes(resolver=resolver, varArgs=varArgs)
		if self.isNamed:
			entries = sorted(entries, key=lambda k: k[2].order)

		# Build the sequence
		from tools.bdl.entities.impl.fragment.type import Type
		for key, item, metadata in entries:
			if onlyTypes:
				Error.assertTrue(element=self.element,
					condition=isinstance(item, Type),
					message="Parameter '{}' is not a type.".format(key))
			if onlyValues:
				Error.assertTrue(element=self.element,
					condition=not isinstance(item, Type),
					message="Parameter '{}' is not a value.".format(key))
			if isinstance(item, str):
				element = ElementBuilder().setAttr(key="value", value=item)
			else:
				element = item.element.copy()
			if self.isNamed:
				ElementBuilder.cast(element, ElementBuilder).setAttr(key="key", value=key)
			sequence.pushBackElement(element)

		return sequence
Exemplo n.º 2
0
 def valueString(self) -> str:
     value = self.value
     if value is None:
         Error.handleFromElement(element=self.element,
                                 message="A value must be present.")
     assert value is not None
     return value
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    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))
Exemplo n.º 5
0
    def visitInclude(self, element: Element) -> ResultType:
        """
		Handle include.
		"""

        includePathStr = self.readValue(element=element)
        Error.assertTrue(
            element=element,
            condition=isinstance(includePathStr, str),
            message="The include path must resolve into a string, instead: '{}'."
            .format(includePathStr))
        path = pathlib.Path(includePathStr)
        paths = [(base / path) for base in self.includeDirs
                 if (base / path).is_file()]
        Error.assertTrue(element=element,
                         condition=len(paths) > 0,
                         message="No valid file '{}' within {}".format(
                             includePathStr,
                             str([f.as_posix() for f in self.includeDirs])))

        template = bzd.template.template.Template(
            template=paths[0].read_text(),
            includeDirs=self.includeDirs,
            indent=self.indent)
        result, substitutions = template._render(
            substitutions=self.substitutions)

        # Update the current substitution object
        self.substitutions.update(substitutions)

        return result
Exemplo n.º 6
0
    def resolve(self, contracts: "Contracts") -> None:
        """
		Merge a base contract into this one. The order is important, as type are inherited from the deepest base.
		"""

        elementBuilder = ElementBuilder.cast(self.element, ElementBuilder)
        for contract in reversed([*contracts]):
            # Check if the type already exists, if so attempt to merge the 2 types.
            existing = self.get(contract.type)
            if existing is not None:
                contractTraits = AllContracts.all.get(contract.type)
                assert contractTraits, "All contracts should have been validated already."
                try:
                    contract = contractTraits.resolveConflict(base=contract,
                                                              derived=existing)
                except Exception as e:
                    Error.handleFromElement(element=self.element,
                                            message=str(e))
                # Remove the existing contract if resolved into a new contract
                if contract is not None:
                    self.remove(contract.type)

            if contract is not None:
                elementBuilder.pushFrontElementToNestedSequence(
                    self.sequenceKind, contract.element)

        # Construct a dummy Validation to check the arguments
        maybeSchema = self.validationForAll
        if maybeSchema:
            Validation(schema=[maybeSchema])
Exemplo n.º 7
0
	def mergeDefaults(self, parameters: "Parameters") -> None:
		"""
		Merge default parameters with this.
		"""
		if not bool(parameters):
			return
		if bool(self):
			if self.isNamed != parameters.isNamed:
				Error.assertTrue(element=self.element,
					condition=bool(self.isNamed),
					message="Requires named parameters.")
				Error.assertTrue(element=self.element,
					condition=not bool(self.isNamed),
					message="Requires unnamed parameters.")

		# Merge the values
		order: int = 0
		for name, expression, metadata in parameters.itemsMetadata():
			if not self.contains(name):
				expression.assertTrue(condition=not expression.contracts.has("mandatory"),
					message="Missing mandatory parameter: '{}'.".format(name))
				self.append(expression, Metadata(default=True))
			# Merge the metadata
			self.atKeyMetadata(name).template = metadata.template
			self.atKeyMetadata(name).order = order
			order += 1
Exemplo n.º 8
0
 def valueNumber(self) -> float:
     try:
         return float(self.valueString)
     except:
         Error.handleFromElement(element=self.element,
                                 message="Expected a valid number.")
     return 0.0  # To make mypy happy
Exemplo n.º 9
0
 def errorSymbolConflict_(element1: Element, element2: Element) -> None:
     message = Error.handleFromElement(
         element=element1,
         message="Symbol name is in conflict...",
         throw=False)
     message += Error.handleFromElement(element=element2,
                                        message="...with this one.",
                                        throw=False)
     raise Exception(message)
Exemplo n.º 10
0
    def parse(self) -> SequenceParser:
        """
		Parse the content using the provided grammar.
		"""

        index = 0
        root = SequenceParser(context=self.context,
                              parent=None,
                              grammar=self.grammar)
        element = root.makeElement()
        checkpoints: MutableMapping[str, Grammar] = {"root": self.grammar}

        # Keep a reference to the content
        assert self.context.content
        content: str = self.context.content

        try:
            while index < len(content):
                m = None
                for item in self.iterateGrammar(self.defaultGrammarPre +
                                                element.getGrammar() +
                                                self.defaultGrammarPost):
                    m = re.match(item.regexpr, content[index:])
                    if m:
                        if item.fragment:
                            fragment = item.fragment(index,
                                                     index + m.end(),
                                                     attrs=m.groupdict())
                            element.add(fragment)
                            grammar = self.getGrammar(checkpoints=checkpoints,
                                                      item=item)
                            element = fragment.next(element, grammar)
                        break
                if m is None:
                    raise Exception("Invalid syntax.")
                assert m is not None
                index += m.end()

        except Exception as e:

            # Uncomment for debug
            print("**** debug ****\n", root)
            for item in self.iterateGrammar(self.defaultGrammarPre +
                                            element.getGrammar() +
                                            self.defaultGrammarPost):
                print(item.regexpr)

            Error.handle(context=self.context,
                         index=index,
                         end=index,
                         message=str(e))

        return root
Exemplo n.º 11
0
	def resolve(self, resolver: "Resolver") -> "EntityType":
		"""
		Resolve the types and nested templates by updating their symbol to fqn.
		"""

		fqns = resolver.resolveFQN(name=self.kind).assertValue(element=self.element, attr=self.kindAttr)

		ElementBuilder.cast(self.element, ElementBuilder).updateAttr(self.kindAttr, ";".join(fqns))

		# Resolve the templates if available
		self.templates.resolve(resolver=resolver)
		templates = self.templates

		# Get and save the underlying type
		underlying = self.getEntityResolved(resolver=resolver)
		# Resolve the entity, this is needed only if the entity is defined after the one holding this type.
		underlying.resolveMemoized(resolver=resolver)

		if self.underlyingTypeAttr is not None and underlying.underlyingType is not None:
			ElementBuilder.cast(self.element, ElementBuilder).setAttr(self.underlyingTypeAttr,
				underlying.underlyingType)

		# Validate template arguments
		configTypes = underlying.getConfigTemplateTypes(resolver=resolver)
		if not configTypes:
			Error.assertTrue(element=self.element,
				condition=(not bool(self.templates)),
				attr=self.kindAttr,
				message="Type '{}' does not support template type arguments.".format(self.kind))
		else:

			templates.mergeDefaults(configTypes)

			# Validate the template arguments
			values = templates.getValuesOrTypesAsDict(resolver=resolver, varArgs=False)
			validation = underlying.makeValidationForTemplate(resolver=resolver, parameters=configTypes)
			assert validation, "Cannot be empty, already checked by the condition."
			resultValidate = validation.validate(values, output="return")
			Error.assertTrue(element=self.element,
				attr=self.kindAttr,
				condition=bool(resultValidate),
				message=str(resultValidate))

			# Save the resolved template only after the validation is completed.
			sequence = templates.toResolvedSequence(resolver=resolver, varArgs=False, onlyTypes=True)
			ElementBuilder.cast(self.element, ElementBuilder).setNestedSequence("{}_resolved".format(self.templateAttr),
				sequence)

		# Resolve contract
		self.contracts.resolve(underlying.contracts)

		return underlying
Exemplo n.º 12
0
    def validate(self) -> None:
        """
		Validate the contracts. it checks the follow:
			- Valid contract types.
		"""

        for contract in self:
            contractTraits = AllContracts.allPublic.get(contract.type)
            if contractTraits is None:
                Error.handleFromElement(
                    element=self.element,
                    message="Contract of type '{}' is not supported.".format(
                        contract.type))
Exemplo n.º 13
0
    def _render(
        self, substitutions: typing.Union[SubstitutionsAccessor,
                                          SubstitutionWrapper]
    ) -> typing.Tuple[ResultType, SubstitutionWrapper]:

        sequence = self.parser.parse()
        *_, last = sequence
        if last.getAttrValue("category",
                             None) in ["if", "else", "for", "macro"]:
            Error.handleFromElement(element=last,
                                    message="Unterminated control block.")

        visitor = Visitor(substitutions, *self.args, **self.kwargs)
        result = visitor.visit(sequence)

        return result, visitor.substitutions
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
 def assertTrue(self,
                condition: bool,
                message: str,
                throw: bool = True) -> typing.Optional[str]:
     return Error.assertTrue(condition=condition,
                             element=self.element,
                             message=message,
                             throw=throw)
Exemplo n.º 16
0
	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
Exemplo n.º 17
0
 def transform(entity: Type, nested: typing.List[str],
               reference: bool) -> TypeConversionCallableReturn:
     maybeContractMin = entity.contracts.get("min")
     isSigned = True if maybeContractMin is None or maybeContractMin.valueNumber < 0 else False
     maybeContractMax = entity.contracts.get("max")
     bits = 32
     if maybeContractMax is not None:
         maxValue = maybeContractMax.valueNumber
         if maxValue < 2**8:
             bits = 8
         elif maxValue < 2**16:
             bits = 16
         elif maxValue < 2**32:
             bits = 32
         elif maxValue < 2**64:
             bits = 64
         else:
             Error.handleFromElement(
                 element=entity.element,
                 message="Value too large, max supported is 64-bit.")
     if isSigned:
         return "bzd::Int{}Type".format(bits), nested
     return "bzd::UInt{}Type".format(bits), nested
Exemplo n.º 18
0
	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
Exemplo n.º 19
0
    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))
Exemplo n.º 20
0
    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
Exemplo n.º 21
0
    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.")
Exemplo n.º 22
0
    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
Exemplo n.º 23
0
 def __init__(self, element: Element) -> None:
     super().__init__(element, Role.Type)
     Error.assertHasAttr(element=element, attr="name")
     Error.assertHasSequence(element=element, sequence="values")
Exemplo n.º 24
0
 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
Exemplo n.º 25
0
    def __init__(self, element: Element) -> None:

        super().__init__(element, Role.Meta)
        Error.assertHasAttr(element=element, attr="value")
Exemplo n.º 26
0
    def __init__(self, element: Element) -> None:

        Error.assertHasAttr(element=element, attr="type")
        self.element = element
Exemplo n.º 27
0
	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))
Exemplo n.º 28
0
    def visitElement(self, element: Element, result: T) -> T:
        """
		Main visitor, called each time a new element is discovered.
		"""

        entity = elementToEntity(element=element,
                                 extension=self.elementToEntityExtenstion)

        # Handle nested object
        if isinstance(entity, Nested):

            self.level += 1

            for category in CATEGORIES:
                sequence = element.getNestedSequence(category)
                if sequence is not None:
                    self.parents.append(
                        Parent(entity=entity, category=category))
                    nestedResult = self._visit(sequence, result)
                    self.entityToNested(category, entity, nestedResult, result)
                    self.parents.pop()

            self.level -= 1

            self.visitNestedEntities(entity, result)

        # Handle expression
        elif isinstance(entity, Expression):

            self.visitExpression(entity, result)

        # Handle method
        elif isinstance(entity, Method):

            self.visitMethod(entity, result)

        # Handle using
        elif isinstance(entity, Using):

            self.visitUsing(entity, result)

        # Handle using
        elif isinstance(entity, Enum):

            self.visitEnum(entity, result)

        # Handle namespace
        elif isinstance(entity, Namespace):

            Error.assertTrue(
                element=element,
                condition=(self.level == 0),
                message="Namespaces can only be declared at top level.")

            self.visitNamespace(entity, result)

            # Update the current namespace
            self.parents.append(Parent(entity=entity))

        # Handle use
        elif isinstance(entity, Use):

            self.visitUse(entity, result)

        # Should never go here
        else:
            Error.handleFromElement(element=element,
                                    message="Unexpected entity: {}".format(
                                        type(entity)))

        return result
Exemplo n.º 29
0
 def __init__(self, element: Element) -> None:
     super().__init__(element, Role.Type)
     Error.assertHasAttr(element=element, attr="name")
Exemplo n.º 30
0
	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