示例#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
示例#2
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
示例#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)
示例#4
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
示例#5
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
示例#6
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)
示例#7
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))
示例#8
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
示例#9
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
示例#10
0
    def resolve(self, resolver: "Resolver") -> None:
        """
		Resolve entities.
		"""
        if self.isValue:
            return

        entity = self.type.resolve(resolver=resolver)

        # Set the underlying value
        if self.isParameters:

            # The type must represent a type (not a value) and have a valid FQN.
            self.assertTrue(
                condition=entity.isRoleType,
                message="Cannot instantiate a value from another value.")
            self.assertTrue(condition=self.isFQN,
                            message="The value must have a FQN.")
            # TODO: it is possible that the expression does not have a FQN, in case the expression is resolved through another entity.
            self._setUnderlyingValue(entity=self, fqn=self.fqn)

        else:
            self._setUnderlyingValue(entity=entity)

        # Generate the argument list and resolve it
        if self.isParameters:
            parameters = self.parameters
            assert parameters is not None
            parameters.resolve(resolver=resolver)
        else:
            parameters = Parameters(element=self.element)

        # Merge its default values
        defaults = self.getConfigValues(resolver=resolver)
        parameters.mergeDefaults(defaults)

        # Read the validation for the value. it comes in part from the direct underlying type, contract information
        # directly associated with this expression do not apply to the current validation.
        validation = self._makeValueValidation(resolver=resolver,
                                               parameters=defaults,
                                               contracts=self.contracts)
        if validation is not None:
            arguments = parameters.getValuesOrTypesAsDict(resolver=resolver,
                                                          varArgs=False)
            result = validation.validate(arguments, output="return")
            Error.assertTrue(element=self.element,
                             attr="type",
                             condition=bool(result),
                             message=str(result))

        # Save the resolved parameters (values and templates), only after the validation is completed.
        argumentValues = parameters.copy(template=False)
        sequence = argumentValues.toResolvedSequence(resolver=resolver,
                                                     varArgs=False,
                                                     onlyValues=True)
        ElementBuilder.cast(self.element, ElementBuilder).setNestedSequence(
            "argument_resolved", sequence)

        argumentTemplates = parameters.copy(template=True)
        sequence = argumentTemplates.toResolvedSequence(resolver=resolver,
                                                        varArgs=False,
                                                        onlyValues=True)
        ElementBuilder.cast(self.element, ElementBuilder).setNestedSequence(
            "argument_template_resolved", sequence)