// buildMapExpression builds the CodeDOM for a map expression.
func (db *domBuilder) buildMapExpression(node compilergraph.GraphNode) codedom.Expression {
	mapScope, _ := db.scopegraph.GetScope(node)
	mapType := mapScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	eit := node.StartQuery().
		Out(parser.NodeMapExpressionChildEntry).
		BuildNodeIterator()

	var keyExprs = make([]codedom.Expression, 0)
	var valueExprs = make([]codedom.Expression, 0)

	for eit.Next() {
		entryNode := eit.Node()

		keyExprs = append(keyExprs, db.getExpression(entryNode, parser.NodeMapExpressionEntryKey))
		valueExprs = append(valueExprs, db.getExpression(entryNode, parser.NodeMapExpressionEntryValue))
	}

	if len(valueExprs) == 0 {
		// Empty map. Call the new() constructor directly.
		constructor, _ := mapType.ResolveMember("new", typegraph.MemberResolutionStatic)
		return codedom.MemberCall(
			codedom.MemberReference(codedom.TypeLiteral(mapType, node), constructor, node),
			constructor,
			[]codedom.Expression{},
			node)
	}

	constructor, _ := mapType.ResolveMember("forArrays", typegraph.MemberResolutionStatic)
	return codedom.MemberCall(
		codedom.MemberReference(codedom.TypeLiteral(mapType, node), constructor, node),
		constructor,
		[]codedom.Expression{codedom.ArrayLiteral(keyExprs, node), codedom.ArrayLiteral(valueExprs, node)},
		node)
}
Beispiel #2
0
func (db *domBuilder) buildLambdaExpressionInternal(node compilergraph.GraphNode, paramPredicate compilergraph.Predicate, body codedom.StatementOrExpression, isGenerator bool) codedom.Expression {
	// Collect the generic names and parameter names of the lambda expression.
	var generics = make([]string, 0)
	var parameters = make([]string, 0)

	git := node.StartQuery().
		Out(parser.NodePredicateTypeMemberGeneric).
		BuildNodeIterator(parser.NodeGenericPredicateName)

	for git.Next() {
		generics = append(generics, git.GetPredicate(parser.NodeGenericPredicateName).String())
	}

	pit := node.StartQuery().
		Out(paramPredicate).
		BuildNodeIterator(parser.NodeLambdaExpressionParameterName)

	for pit.Next() {
		parameters = append(parameters, pit.GetPredicate(parser.NodeLambdaExpressionParameterName).String())
	}

	// Check for a generator.
	specialization := codedom.NormalFunction
	if isGenerator {
		specialization = codedom.GeneratorFunction
	}

	return codedom.FunctionDefinition(generics, parameters, body, false, specialization, node)
}
Beispiel #3
0
// scopeInlineLambaExpression scopes an inline lambda expression node in the SRG.
func (sb *scopeBuilder) scopeInlineLambaExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var returnType = sb.sg.tdg.AnyTypeReference()

	// Scope the lambda's internal expression.
	exprScope := sb.getScope(node.GetNode(parser.NodeLambdaExpressionChildExpr), context)
	if exprScope.GetIsValid() {
		returnType = exprScope.ResolvedTypeRef(sb.sg.tdg)
	}

	// Build the function type.
	var functionType = sb.sg.tdg.FunctionTypeReference(returnType)

	// Add the parameter types.
	pit := node.StartQuery().
		Out(parser.NodeLambdaExpressionInferredParameter).
		BuildNodeIterator()

	for pit.Next() {
		parameterType, hasParameterType := sb.inferredParameterTypes.Get(string(pit.Node().NodeId))
		if hasParameterType {
			functionType = functionType.WithParameter(parameterType.(typegraph.TypeReference))
		} else {
			functionType = functionType.WithParameter(sb.sg.tdg.AnyTypeReference())
		}
	}

	return newScope().IsValid(exprScope.GetIsValid()).Resolving(functionType).GetScope()
}
// scopeListLiteralExpression scopes a list literal expression in the SRG.
func (sb *scopeBuilder) scopeListLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true
	var valueType = sb.sg.tdg.VoidTypeReference()

	// Scope each of the expressions and determine the list type based on its contents.
	vit := node.StartQuery().
		Out(parser.NodeListExpressionValue).
		BuildNodeIterator()

	for vit.Next() {
		valueNode := vit.Node()
		valueScope := sb.getScope(valueNode, context)
		if !valueScope.GetIsValid() {
			isValid = false
		} else {
			valueType = valueType.Intersect(valueScope.ResolvedTypeRef(sb.sg.tdg))
		}
	}

	if valueType.IsVoid() {
		valueType = sb.sg.tdg.AnyTypeReference()
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.ListTypeReference(valueType)).GetScope()
}
// scopeSliceLiteralExpression scopes a slice literal expression in the SRG.
func (sb *scopeBuilder) scopeSliceLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true

	declaredTypeNode := node.GetNode(parser.NodeSliceLiteralExpressionType)
	declaredType, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(declaredTypeNode))
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().Resolving(sb.sg.tdg.SliceTypeReference(sb.sg.tdg.AnyTypeReference())).GetScope()
	}

	// Scope each of the expressions and ensure they match the slice type.
	vit := node.StartQuery().
		Out(parser.NodeSliceLiteralExpressionValue).
		BuildNodeIterator()

	for vit.Next() {
		valueNode := vit.Node()
		valueScope := sb.getScope(valueNode, context)
		if !valueScope.GetIsValid() {
			isValid = false
		} else {
			if serr := valueScope.ResolvedTypeRef(sb.sg.tdg).CheckSubTypeOf(declaredType); serr != nil {
				isValid = false
				sb.decorateWithError(node, "Invalid slice literal value: %v", serr)
			}
		}
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.SliceTypeReference(declaredType)).GetScope()
}
// buildMappingLiteralExpression builds the CodeDOM for a mapping literal expression.
func (db *domBuilder) buildMappingLiteralExpression(node compilergraph.GraphNode) codedom.Expression {
	mappingScope, _ := db.scopegraph.GetScope(node)
	mappingType := mappingScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	eit := node.StartQuery().
		Out(parser.NodeMappingLiteralExpressionEntryRef).
		BuildNodeIterator()

	var entries = make([]codedom.ObjectLiteralEntryNode, 0)

	for eit.Next() {
		entryNode := eit.Node()

		// The key expression must be a string when produced. We either reference it directly (if a string)
		// or call .String() (if a Stringable).
		keyNode := entryNode.GetNode(parser.NodeMappingLiteralExpressionEntryKey)
		keyScope, _ := db.scopegraph.GetScope(keyNode)
		keyType := keyScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

		var keyExpr = db.buildExpression(keyNode)
		if !keyType.HasReferredType(db.scopegraph.TypeGraph().StringType()) {
			stringMethod, _ := keyType.ResolveMember("String", typegraph.MemberResolutionInstance)

			keyExpr = codedom.MemberCall(
				codedom.MemberReference(db.buildExpression(keyNode), stringMethod, node),
				stringMethod,
				[]codedom.Expression{},
				keyNode)
		}

		// Get the expression for the value.
		valueExpr := db.getExpression(entryNode, parser.NodeMappingLiteralExpressionEntryValue)

		// Build an object literal expression with the (native version of the) key string and the
		// created value.
		entryExpr := codedom.ObjectLiteralEntryNode{
			codedom.NominalUnwrapping(keyExpr, db.scopegraph.TypeGraph().StringTypeReference(), keyNode),
			valueExpr,
			entryNode,
		}
		entries = append(entries, entryExpr)
	}

	if len(entries) == 0 {
		// Empty mapping. Call the Empty() constructor directly.
		constructor, _ := mappingType.ResolveMember("Empty", typegraph.MemberResolutionStatic)
		return codedom.MemberCall(
			codedom.MemberReference(codedom.TypeLiteral(mappingType, node), constructor, node),
			constructor,
			[]codedom.Expression{},
			node)
	}

	constructor, _ := mappingType.ResolveMember("overObject", typegraph.MemberResolutionStatic)
	return codedom.MemberCall(
		codedom.MemberReference(codedom.TypeLiteral(mappingType, node), constructor, node),
		constructor,
		[]codedom.Expression{codedom.ObjectLiteral(entries, node)},
		node)
}
// buildStructuralNewExpression builds the CodeDOM for a structural new expression.
func (db *domBuilder) buildStructuralNewExpression(node compilergraph.GraphNode) codedom.Expression {
	// Collect the full set of initializers, by member.
	initializers := map[string]codedom.Expression{}
	eit := node.StartQuery().
		Out(parser.NodeStructuralNewExpressionChildEntry).
		BuildNodeIterator()

	for eit.Next() {
		entryScope, _ := db.scopegraph.GetScope(eit.Node())
		entryName, _ := db.scopegraph.GetReferencedName(entryScope)
		entryMember, _ := entryName.Member()

		initializers[entryMember.Name()] = db.getExpression(eit.Node(), parser.NodeStructuralNewEntryValue)
	}

	childScope, _ := db.scopegraph.GetScope(node.GetNode(parser.NodeStructuralNewTypeExpression))

	nodeScope, _ := db.scopegraph.GetScope(node)
	if nodeScope.HasLabel(proto.ScopeLabel_STRUCTURAL_UPDATE_EXPR) {
		// Build a call to the Clone() method followed by assignments.
		resolvedTypeRef := childScope.ResolvedTypeRef(db.scopegraph.TypeGraph())
		return db.buildStructCloneExpression(resolvedTypeRef, initializers, node)
	} else {
		// Build a call to the new() constructor of the type with the required field expressions.
		staticTypeRef := childScope.StaticTypeRef(db.scopegraph.TypeGraph())
		return db.buildStructInitializerExpression(staticTypeRef, initializers, node)
	}
}
// buildCollectionLiteralExpression builds a literal collection expression.
func (db *domBuilder) buildCollectionLiteralExpression(node compilergraph.GraphNode, valuePredicate compilergraph.Predicate, emptyConstructorName string, arrayConstructorName string) codedom.Expression {
	collectionScope, _ := db.scopegraph.GetScope(node)
	collectionType := collectionScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	vit := node.StartQuery().
		Out(valuePredicate).
		BuildNodeIterator()

	valueExprs := db.buildExpressions(vit, buildExprNormally)
	return db.buildCollectionInitializerExpression(collectionType, valueExprs, emptyConstructorName, arrayConstructorName, node)
}
// buildTemplateStringCall builds the CodeDOM representing the call to a template string function.
func (db *domBuilder) buildTemplateStringCall(node compilergraph.GraphNode, funcExpr codedom.Expression, isTagged bool) codedom.Expression {
	pit := node.StartQuery().
		Out(parser.NodeTemplateStringPiece).
		BuildNodeIterator()

	var pieceExprs = make([]codedom.Expression, 0)
	var valueExprs = make([]codedom.Expression, 0)

	var isPiece = true
	for pit.Next() {
		if isPiece {
			pieceExprs = append(pieceExprs, db.buildExpression(pit.Node()))
		} else {
			valueExprs = append(valueExprs, db.buildExpression(pit.Node()))
		}

		isPiece = !isPiece
	}

	// Handle common case: No literal string piece at all.
	if len(pieceExprs) == 0 {
		return codedom.NominalWrapping(codedom.LiteralValue("''", node), db.scopegraph.TypeGraph().StringType(), node)
	}

	// Handle common case: A single literal string piece with no values.
	if len(pieceExprs) == 1 && len(valueExprs) == 0 {
		return pieceExprs[0]
	}

	pieceSliceType := db.scopegraph.TypeGraph().SliceTypeReference(db.scopegraph.TypeGraph().StringTypeReference())
	valueSliceType := db.scopegraph.TypeGraph().SliceTypeReference(db.scopegraph.TypeGraph().StringableTypeReference())

	constructor, _ := pieceSliceType.ResolveMember("overArray", typegraph.MemberResolutionStatic)

	pieceSliceExpr := codedom.MemberCall(
		codedom.MemberReference(
			codedom.TypeLiteral(pieceSliceType, node), constructor, node),
		constructor,
		[]codedom.Expression{codedom.ArrayLiteral(pieceExprs, node)},
		node)

	valueSliceExpr := codedom.MemberCall(
		codedom.MemberReference(
			codedom.TypeLiteral(valueSliceType, node), constructor, node),
		constructor,
		[]codedom.Expression{codedom.ArrayLiteral(valueExprs, node)},
		node)

	return codedom.AwaitPromise(codedom.FunctionCall(funcExpr, []codedom.Expression{pieceSliceExpr, valueSliceExpr}, node), node)
}
Beispiel #10
0
// scopeTypeConversionExpression scopes a conversion from a nominal type to a base type.
func (sb *scopeBuilder) scopeTypeConversionExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	childScope := sb.getScope(node.GetNode(parser.NodeFunctionCallExpressionChildExpr), context)
	conversionType := childScope.StaticTypeRef(sb.sg.tdg)

	// Ensure that the function call has a single argument.
	ait := node.StartQuery().
		Out(parser.NodeFunctionCallArgument).
		BuildNodeIterator()

	var index = 0
	var isValid = true

	for ait.Next() {
		if index > 0 {
			sb.decorateWithError(node, "Type conversion requires a single argument")
			isValid = false
			break
		}

		index = index + 1

		// Make sure the argument's scope is valid.
		argumentScope := sb.getScope(ait.Node(), context)
		if !argumentScope.GetIsValid() {
			isValid = false
			continue
		}

		// The argument must be a nominal subtype of the conversion type.
		argumentType := argumentScope.ResolvedTypeRef(sb.sg.tdg)
		if nerr := argumentType.CheckNominalConvertable(conversionType); nerr != nil {
			sb.decorateWithError(node, "Cannot perform type conversion: %v", nerr)
			isValid = false
			break
		}

		if argumentType.IsNullable() {
			conversionType = conversionType.AsNullable()
		}
	}

	if index == 0 {
		sb.decorateWithError(node, "Type conversion requires a single argument")
		isValid = false
	}

	// The type conversion returns an instance of the converted type.
	return newScope().IsValid(isValid).Resolving(conversionType).GetScope()
}
Beispiel #11
0
// buildGenericSpecifierExpression builds the CodeDOM for a generic specification of a function or type.
func (db *domBuilder) buildGenericSpecifierExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := db.getExpression(node, parser.NodeGenericSpecifierChildExpr)

	// Collect the generic types being specified.
	git := node.StartQuery().
		Out(parser.NodeGenericSpecifierType).
		BuildNodeIterator()

	var genericTypes = make([]codedom.Expression, 0)
	for git.Next() {
		replacementType, _ := db.scopegraph.ResolveSRGTypeRef(db.scopegraph.SourceGraph().GetTypeRef(git.Node()))
		genericTypes = append(genericTypes, codedom.TypeLiteral(replacementType, git.Node()))
	}

	return codedom.FunctionCall(childExpr, genericTypes, node)
}
Beispiel #12
0
// scopeMappingLiteralExpression scopes a mapping literal expression in the SRG.
func (sb *scopeBuilder) scopeMappingLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true

	declaredTypeNode := node.GetNode(parser.NodeMappingLiteralExpressionType)
	declaredType, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(declaredTypeNode))
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().Resolving(sb.sg.tdg.MappingTypeReference(sb.sg.tdg.AnyTypeReference())).GetScope()
	}

	// Scope each of the entries and ensure they match the mapping value type.
	eit := node.StartQuery().
		Out(parser.NodeMappingLiteralExpressionEntryRef).
		BuildNodeIterator()

	for eit.Next() {
		entryNode := eit.Node()

		keyNode := entryNode.GetNode(parser.NodeMappingLiteralExpressionEntryKey)
		valueNode := entryNode.GetNode(parser.NodeMappingLiteralExpressionEntryValue)

		keyScope := sb.getScope(keyNode, context)
		valueScope := sb.getScope(valueNode, context)

		if keyScope.GetIsValid() {
			localKeyType := keyScope.ResolvedTypeRef(sb.sg.tdg)
			if serr := localKeyType.CheckSubTypeOf(sb.sg.tdg.StringableTypeReference()); serr != nil {
				sb.decorateWithError(keyNode, "Mapping literal keys must be of type Stringable: %v", serr)
				isValid = false
			}
		} else {
			isValid = false
		}

		if valueScope.GetIsValid() {
			localValueType := valueScope.ResolvedTypeRef(sb.sg.tdg)
			if serr := localValueType.CheckSubTypeOf(declaredType); serr != nil {
				sb.decorateWithError(keyNode, "Expected mapping values of type %v: %v", declaredType, serr)
				isValid = false
			}
		} else {
			isValid = false
		}
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.MappingTypeReference(declaredType)).GetScope()
}
Beispiel #13
0
// buildFunctionCall builds the CodeDOM for a function call.
func (db *domBuilder) buildFunctionCall(node compilergraph.GraphNode) codedom.Expression {
	childExprNode := node.GetNode(parser.NodeFunctionCallExpressionChildExpr)
	childScope, _ := db.scopegraph.GetScope(childExprNode)

	// Check if the child expression has a static scope. If so, this is a type conversion between
	// a nominal type and a base type.
	if childScope.GetKind() == proto.ScopeKind_STATIC {
		wrappedExprNode := node.GetNode(parser.NodeFunctionCallArgument)
		wrappedExprScope, _ := db.scopegraph.GetScope(wrappedExprNode)
		wrappedExprType := wrappedExprScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

		wrappedExpr := db.buildExpression(wrappedExprNode)

		targetTypeRef := childScope.StaticTypeRef(db.scopegraph.TypeGraph())

		// If the targetTypeRef is not nominal or structural, then we know we are unwrapping.
		if !targetTypeRef.IsNominalOrStruct() {
			return codedom.NominalUnwrapping(wrappedExpr, wrappedExprType, node)
		} else {
			return codedom.NominalRefWrapping(wrappedExpr, wrappedExprType, targetTypeRef, node)
		}
	}

	// Collect the expressions for the arguments.
	ait := node.StartQuery().
		Out(parser.NodeFunctionCallArgument).
		BuildNodeIterator()

	arguments := db.buildExpressions(ait, buildExprCheckNominalShortcutting)
	childExpr := db.buildExpression(childExprNode)

	// If the function call is to a member, then we return a MemberCall.
	namedRef, isNamed := db.scopegraph.GetReferencedName(childScope)
	if isNamed && !namedRef.IsLocal() {
		member, _ := namedRef.Member()

		if childExprNode.Kind() == parser.NodeNullableMemberAccessExpression {
			return codedom.NullableMemberCall(childExpr, member, arguments, node)
		}

		return codedom.MemberCall(childExpr, member, arguments, node)
	}

	// Otherwise, this is a normal function call with an await.
	return codedom.AwaitPromise(codedom.FunctionCall(childExpr, arguments, node), node)
}
Beispiel #14
0
// scopeMapLiteralExpression scopes a map literal expression in the SRG.
func (sb *scopeBuilder) scopeMapLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true
	var keyType = sb.sg.tdg.VoidTypeReference()
	var valueType = sb.sg.tdg.VoidTypeReference()

	// Scope each of the entries and determine the map key and value types based on the entries found.
	eit := node.StartQuery().
		Out(parser.NodeMapExpressionChildEntry).
		BuildNodeIterator()

	for eit.Next() {
		entryNode := eit.Node()

		keyNode := entryNode.GetNode(parser.NodeMapExpressionEntryKey)
		valueNode := entryNode.GetNode(parser.NodeMapExpressionEntryValue)

		keyScope := sb.getScope(keyNode, context)
		valueScope := sb.getScope(valueNode, context)

		if !keyScope.GetIsValid() || !valueScope.GetIsValid() {
			isValid = false
			continue
		}

		localKeyType := keyScope.ResolvedTypeRef(sb.sg.tdg)
		localValueType := valueScope.ResolvedTypeRef(sb.sg.tdg)

		if serr := localKeyType.CheckSubTypeOf(sb.sg.tdg.MappableTypeReference()); serr != nil {
			sb.decorateWithError(keyNode, "Map literal keys must be of type Mappable: %v", serr)
			isValid = false
		}

		keyType = keyType.Intersect(localKeyType)
		valueType = valueType.Intersect(localValueType)
	}

	if keyType.IsVoid() || keyType.IsAny() {
		keyType = sb.sg.tdg.MappableTypeReference()
	}

	if valueType.IsVoid() {
		valueType = sb.sg.tdg.AnyTypeReference()
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.MapTypeReference(keyType, valueType)).GetScope()
}
Beispiel #15
0
// getDeclaredVariableType returns the declared type of a variable statement, member or type field (if any).
func (sb *scopeBuilder) getDeclaredVariableType(node compilergraph.GraphNode) (typegraph.TypeReference, bool) {
	declaredTypeNode, hasDeclaredType := node.StartQuery().
		Out(parser.NodeVariableStatementDeclaredType, parser.NodePredicateTypeMemberDeclaredType).
		TryGetNode()

	if !hasDeclaredType {
		return sb.sg.tdg.AnyTypeReference(), false
	}

	// Load the declared type.
	declaredType, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(declaredTypeNode))
	if rerr != nil {
		panic(rerr)
		return sb.sg.tdg.AnyTypeReference(), false
	}

	return declaredType, true
}
Beispiel #16
0
// getFunctionCallArgumentTypes returns the resolved types of the argument expressions to the given function
// call.
func (sb *scopeBuilder) getFunctionCallArgumentTypes(node compilergraph.GraphNode, context scopeContext) []typegraph.TypeReference {
	ait := node.StartQuery().
		Out(parser.NodeFunctionCallArgument).
		BuildNodeIterator()

	var types = make([]typegraph.TypeReference, 0)
	for ait.Next() {
		// Resolve the scope of the argument.
		argumentScope := sb.getScope(ait.Node(), context)
		if !argumentScope.GetIsValid() {
			continue
		}

		types = append(types, argumentScope.ResolvedTypeRef(sb.sg.tdg))
	}

	return types
}
Beispiel #17
0
// scopeFullLambaExpression scopes a fully defined lambda expression node in the SRG.
func (sb *scopeBuilder) scopeFullLambaExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var returnType = sb.sg.tdg.AnyTypeReference()

	// Check for a defined return type for the lambda expression.
	returnTypeNode, hasReturnType := node.TryGetNode(parser.NodeLambdaExpressionReturnType)
	if hasReturnType {
		resolvedReturnType, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(returnTypeNode))
		if rerr != nil {
			panic(rerr)
		}

		returnType = resolvedReturnType
	}

	// Scope the block. If the function has no defined return type, we use the return type of the block.
	blockScope := sb.getScope(node.GetNode(parser.NodeLambdaExpressionBlock), context.withImplemented(node))
	if !hasReturnType && blockScope.GetIsValid() {
		returnType = blockScope.ReturnedTypeRef(sb.sg.tdg)
	}

	// Build the function type.
	var functionType = sb.sg.tdg.FunctionTypeReference(returnType)

	// Add the parameter types.
	pit := node.StartQuery().
		Out(parser.NodeLambdaExpressionParameter).
		BuildNodeIterator()

	for pit.Next() {
		parameterTypeNode := pit.Node().GetNode(parser.NodeParameterType)
		parameterType, perr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(parameterTypeNode))
		if perr != nil {
			panic(perr)
		}

		functionType = functionType.WithParameter(parameterType)
	}

	return newScope().IsValid(blockScope.GetIsValid()).Resolving(functionType).GetScope()
}
Beispiel #18
0
// adjustedMemberSignature returns the member signature found on the given node, adjusted for
// the parent type's generics, as specified in this type reference. Will panic if the type reference
// does not refer to the node's parent type.
func (tr TypeReference) adjustedMemberSignature(node compilergraph.GraphNode) string {
	compilerutil.DCHECK(func() bool {
		return node.StartQuery().In(NodePredicateMember).GetNode() == tr.referredTypeNode()
	}, "Type reference must be parent of member node")

	// Retrieve the generics of the parent type.
	parentNode := tr.referredTypeNode()
	pgit := parentNode.StartQuery().Out(NodePredicateTypeGeneric).BuildNodeIterator()

	// Parse the member signature.
	esig := &proto.MemberSig{}
	memberSig := node.GetTagged(NodePredicateMemberSignature, esig).(*proto.MemberSig)

	// Replace the generics of the parent type in the signature with those of the type reference.
	generics := tr.Generics()

	var index = 0
	var memberType = tr.Build(memberSig.GetMemberType()).(TypeReference)
	for pgit.Next() {
		genericNode := pgit.Node()
		genericRef := generics[index]
		genericType := TGTypeDecl{genericNode, tr.tdg}

		// Replace the generic in the member type.
		memberType = memberType.ReplaceType(genericType, genericRef)

		// Replace the generic in any generic constraints.
		for cindex, constraint := range memberSig.GetGenericConstraints() {
			memberSig.GenericConstraints[cindex] = tr.Build(constraint).(TypeReference).
				ReplaceType(genericType, genericRef).
				Value()
		}

		index = index + 1
	}

	adjustedType := memberType.Value()
	memberSig.MemberType = &adjustedType
	return memberSig.Value()
}
Beispiel #19
0
// buildStatementBlock builds the CodeDOM for a statement block.
func (db *domBuilder) buildStatementBlock(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) {
	sit := node.StartQuery().
		Out(parser.NodeStatementBlockStatement).
		BuildNodeIterator()

	startStatement := codedom.EmptyStatement(node)

	var current codedom.Statement = startStatement
	for sit.Next() {
		startStatement, endStatement := db.buildStatements(sit.Node())
		codedom.AssignNextStatement(current, startStatement)
		current = endStatement

		// If the current node is a terminating statement, skip the rest of the block.
		scope, hasScope := db.scopegraph.GetScope(sit.Node())
		if hasScope && scope.GetIsTerminatingStatement() {
			break
		}
	}

	return startStatement, current
}
Beispiel #20
0
// scopeStructuralNewEntries scopes all the entries of a structural new expression.
func (sb *scopeBuilder) scopeStructuralNewEntries(node compilergraph.GraphNode, context scopeContext) (map[string]bool, bool) {
	// Scope the defined entries. We also build a list here to ensure all required entries are
	// added.
	encountered := map[string]bool{}
	eit := node.StartQuery().
		Out(parser.NodeStructuralNewExpressionChildEntry).
		BuildNodeIterator(parser.NodeStructuralNewEntryKey)

	var isValid = true
	for eit.Next() {
		// Scope the entry.
		entryName := eit.GetPredicate(parser.NodeStructuralNewEntryKey).String()
		entryScope := sb.getScope(eit.Node(), context)
		if !entryScope.GetIsValid() {
			isValid = false
		}

		encountered[entryName] = true
	}

	return encountered, isValid
}
Beispiel #21
0
// scopeTemplateStringExpression scopes a template string expression in the SRG.
func (sb *scopeBuilder) scopeTemplateStringExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope each of the pieces of the template string. All pieces must be strings or Stringable.
	pit := node.StartQuery().
		Out(parser.NodeTemplateStringPiece).
		BuildNodeIterator()

	var isValid = true
	for pit.Next() {
		pieceNode := pit.Node()
		pieceScope := sb.getScope(pieceNode, context)
		if !pieceScope.GetIsValid() {
			isValid = false
			continue
		}

		pieceType := pieceScope.ResolvedTypeRef(sb.sg.tdg)
		if serr := pieceType.CheckSubTypeOf(sb.sg.tdg.StringableTypeReference()); serr != nil {
			isValid = false
			sb.decorateWithError(pieceNode, "All expressions in a template string must be of type Stringable: %v", serr)
		}
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.StringTypeReference()).GetScope()
}
Beispiel #22
0
// scopeFunctionCallExpression scopes a function call expression in the SRG.
func (sb *scopeBuilder) scopeFunctionCallExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope the child expression.
	childExpr := node.GetNode(parser.NodeFunctionCallExpressionChildExpr)
	childScope := sb.getScope(childExpr, context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Check if the child expression has a static scope. If so, this is a type conversion between
	// a nominal type and a base type.
	if childScope.GetKind() == proto.ScopeKind_STATIC {
		return sb.scopeTypeConversionExpression(node, context)
	} else if childScope.GetKind() == proto.ScopeKind_GENERIC {
		namedScopedRef, found := sb.getNamedScopeForScope(childScope)
		if found {
			sb.decorateWithError(node, "Cannot invoke function call on unclarified generic %s %s.", namedScopedRef.Title(), namedScopedRef.Name())
		} else {
			sb.decorateWithError(node, "Cannot invoke function call on unclarified generic scope.")
		}
		return newScope().Invalid().GetScope()
	}

	namedNode, hasNamedNode := sb.getNamedScopeForScope(childScope)
	if hasNamedNode {
		context.staticDependencyCollector.registerNamedDependency(namedNode)
	}

	getDescription := func() string {
		if !hasNamedNode {
			return ""
		}

		return fmt.Sprintf("on %v %v ", namedNode.Title(), namedNode.Name())
	}

	// Ensure the child expression has type function.
	childType := childScope.ResolvedTypeRef(sb.sg.tdg)
	if !childType.IsDirectReferenceTo(sb.sg.tdg.FunctionType()) {
		// If the child type is a function, but nullable, only allow it to be called if the type
		// is a result of a null access expression. This is a special case to allow writing code
		// such as `foo?.bar()` easier, without allowing for random `someNullableFunc()` to be
		// called.
		//
		// TODO: It might be a good idea to revisit this decision if we find `someNullableFunc()` to
		// be a useful pattern as well.
		if !childType.HasReferredType(sb.sg.tdg.FunctionType()) ||
			childExpr.Kind() != parser.NodeNullableMemberAccessExpression {
			sb.decorateWithError(node, "Cannot invoke function call on non-function '%v'.", childType)
			return newScope().Invalid().GetScope()
		}
	}

	// Find the starting index of the nullable parameters. Once all parameters are nullable,
	// they are considered optional.
	var nonOptionalIndex = -1
	childParameters := childType.Parameters()
	for parameterIndex, parameterType := range childParameters {
		if !parameterType.NullValueAllowed() {
			nonOptionalIndex = parameterIndex
		}
	}

	// Ensure that the parameters of the function call match those of the child type.
	var index = -1
	ait := node.StartQuery().
		Out(parser.NodeFunctionCallArgument).
		BuildNodeIterator()

	var isValid = true
	for ait.Next() {
		index = index + 1

		// Resolve the scope of the argument.
		argumentScope := sb.getScope(ait.Node(), context)
		if !argumentScope.GetIsValid() {
			isValid = false
			nonOptionalIndex = index
			continue
		}

		if index < len(childParameters) {
			// Ensure the type of the argument matches the parameter.
			argumentType := argumentScope.ResolvedTypeRef(sb.sg.tdg)
			serr, exception := argumentType.CheckSubTypeOfWithExceptions(childParameters[index], typegraph.AllowNominalWrappedForData)
			if serr != nil {
				sb.decorateWithError(ait.Node(), "Parameter #%v %sexpects type %v: %v", index+1, getDescription(), childParameters[index], serr)
				isValid = false
			}

			// If a nominally-wrapped value was used in place of an argument that expects its data type, then
			// mark the expression as being a shortcut that needs unwrapping during generation.
			if exception == typegraph.AllowNominalWrappedForData {
				sb.decorateWithSecondaryLabel(ait.Node(), proto.ScopeLabel_NOMINALLY_SHORTCUT_EXPR)
			}
		}
	}

	if index < nonOptionalIndex {
		sb.decorateWithError(node, "Function call %sexpects %v non-optional arguments, found %v", getDescription(), nonOptionalIndex+1, index+1)
		return newScope().Invalid().GetScope()
	}

	if index >= len(childParameters) {
		sb.decorateWithError(node, "Function call %sexpects %v arguments, found %v", getDescription(), len(childParameters), index+1)
		return newScope().Invalid().GetScope()
	}

	var returnType = childType.Generics()[0]
	if childType.IsNullable() {
		returnType = returnType.AsNullable()
	}

	// Check for an awaitable return type. If found and this call is not under an assignment or
	// arrow, warn.
	if isValid && returnType.IsDirectReferenceTo(sb.sg.tdg.AwaitableType()) {
		if !returnType.Generics()[0].IsVoid() {
			if _, underStatement := node.TryGetIncomingNode(parser.NodeExpressionStatementExpression); underStatement {
				sb.decorateWithWarning(node, "Returned Awaitable resolves a value of type %v which is not handled", returnType.Generics()[0])
			}
		}
	}

	// The function call returns the first generic of the function.
	return newScope().IsValid(isValid).Resolving(returnType).GetScope()
}
Beispiel #23
0
// scopeMatchStatement scopes a match statement in the SRG.
func (sb *scopeBuilder) scopeMatchStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope the match expression.
	matchExprScope := sb.getScope(node.GetNode(parser.NodeMatchStatementExpression), context)
	if !matchExprScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	matchExprType := matchExprScope.ResolvedTypeRef(sb.sg.tdg)

	// Scope each of the case statements under the match, ensuring that their type is a subtype
	// of the match expression's type.
	var returnedType = sb.sg.tdg.VoidTypeReference()
	var settlesScope = true
	var hasDefault = false
	var labelSet = newLabelSet()
	var isValid = true

	// Lookup the named value, if any.
	matchNamedValue, hasNamedValue := node.TryGetNode(parser.NodeStatementNamedValue)

	sit := node.StartQuery().
		Out(parser.NodeMatchStatementCase).
		BuildNodeIterator()

	for sit.Next() {
		var matchBranchType = sb.sg.tdg.AnyTypeReference()

		// Check the case's type reference (if any) against the type expected.
		caseTypeRefNode, hasCaseTypeRef := sit.Node().TryGetNode(parser.NodeMatchStatementCaseTypeReference)
		if hasCaseTypeRef {
			matchTypeRef, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(caseTypeRefNode))
			if rerr != nil {
				panic(rerr)
			}

			// Ensure that the type is not nullable, as then a null could match anything.
			if matchTypeRef.IsNullable() {
				sb.decorateWithError(node, "Match cases cannot be nullable. Found: %v", matchTypeRef)
				isValid = false
			} else if serr := matchTypeRef.CheckCastableFrom(matchExprType); serr != nil {
				// Ensure that the type is a subtype of the expression type.
				sb.decorateWithError(node, "Match cases must be castable from type '%v': %v", matchExprType, serr)
				isValid = false
			} else {
				matchBranchType = matchTypeRef
			}
		} else {
			hasDefault = true
		}

		// Build the local context for scoping. If this match has an 'as', then its type is overridden
		// to the match type for each branch.
		localContext := context
		if hasNamedValue {
			localContext = context.withTypeOverride(matchNamedValue, matchBranchType)
		}

		// Scope the statement block under the case.
		statementBlockNode := sit.Node().GetNode(parser.NodeMatchStatementCaseStatement)
		statementBlockScope := sb.getScope(statementBlockNode, localContext.withBreakable(node))
		if !statementBlockScope.GetIsValid() {
			isValid = false
		}

		labelSet.AppendLabelsOf(statementBlockScope)

		returnedType = returnedType.Intersect(statementBlockScope.ReturnedTypeRef(sb.sg.tdg))
		settlesScope = settlesScope && statementBlockScope.GetIsSettlingScope()
	}

	// If there isn't a default case, then the match cannot be known to return in all cases.
	if !hasDefault {
		returnedType = sb.sg.tdg.VoidTypeReference()
		settlesScope = false
	}

	return newScope().IsValid(isValid).Returning(returnedType, settlesScope).WithLabelSet(labelSet).GetScope()
}
Beispiel #24
0
// scopeSwitchStatement scopes a switch statement in the SRG.
func (sb *scopeBuilder) scopeSwitchStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Check for an expression. If a switch has an expression, then all cases must match against it.
	var isValid = true
	var switchValueType = sb.sg.tdg.BoolTypeReference()

	exprNode, hasExpression := node.TryGetNode(parser.NodeSwitchStatementExpression)
	if hasExpression {
		exprScope := sb.getScope(exprNode, context)
		if exprScope.GetIsValid() {
			switchValueType = exprScope.ResolvedTypeRef(sb.sg.tdg)
		} else {
			isValid = false
		}
	}

	// Ensure that the switch type has a defined accessible comparison operator.
	if isValid {
		module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
		_, rerr := switchValueType.ResolveAccessibleMember("equals", module, typegraph.MemberResolutionOperator)
		if rerr != nil {
			sb.decorateWithError(node, "Cannot switch over instance of type '%v', as it does not define or export an 'equals' operator", switchValueType)
			isValid = false
		}
	}

	// Scope each of the case statements under the switch.
	var returnedType = sb.sg.tdg.VoidTypeReference()
	var settlesScope = true
	var hasDefault = false
	var labelSet = newLabelSet()

	sit := node.StartQuery().
		Out(parser.NodeSwitchStatementCase).
		BuildNodeIterator()

	for sit.Next() {
		// Scope the statement block under the case.
		statementBlockNode := sit.Node().GetNode(parser.NodeSwitchStatementCaseStatement)
		statementBlockScope := sb.getScope(statementBlockNode, context.withBreakable(node))
		if !statementBlockScope.GetIsValid() {
			isValid = false
		}

		labelSet.AppendLabelsOf(statementBlockScope)

		returnedType = returnedType.Intersect(statementBlockScope.ReturnedTypeRef(sb.sg.tdg))
		settlesScope = settlesScope && statementBlockScope.GetIsSettlingScope()

		// Check the case's expression (if any) against the type expected.
		caseExprNode, hasCaseExpression := sit.Node().TryGetNode(parser.NodeSwitchStatementCaseExpression)
		if hasCaseExpression {
			caseExprScope := sb.getScope(caseExprNode, context)
			if caseExprScope.GetIsValid() {
				caseExprType := caseExprScope.ResolvedTypeRef(sb.sg.tdg)
				if serr := caseExprType.CheckSubTypeOf(switchValueType); serr != nil {
					sb.decorateWithError(node, "Switch cases must have values matching type '%v': %v", switchValueType, serr)
					isValid = false
				}
			} else {
				isValid = false
			}
		} else {
			hasDefault = true
		}
	}

	// If there isn't a default case, then the switch cannot be known to return in all cases.
	if !hasDefault {
		returnedType = sb.sg.tdg.VoidTypeReference()
		settlesScope = false
	}

	return newScope().IsValid(isValid).Returning(returnedType, settlesScope).WithLabelSet(labelSet).GetScope()
}
Beispiel #25
0
// scopeStatementBlock scopes a block of statements in the SRG.
func (sb *scopeBuilder) scopeStatementBlock(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	sit := node.StartQuery().
		Out(parser.NodeStatementBlockStatement).
		BuildNodeIterator()

	// Scope all the child statements, collecting the types returned along the way.
	var returnedType = sb.sg.tdg.VoidTypeReference()
	var isSettlingScope = false
	var isValid = true
	var skipRemaining = false
	var unreachableWarned = false
	var labelSet = newLabelSet()

	for sit.Next() {
		statementScope := sb.getScope(sit.Node(), context)
		labelSet.AppendLabelsOf(statementScope)

		if skipRemaining {
			if !unreachableWarned {
				sb.decorateWithWarning(node, "Unreachable statement found")
			}

			unreachableWarned = true
			continue
		}

		if !statementScope.GetIsValid() {
			isValid = false
		}

		returnedType = returnedType.Intersect(statementScope.ReturnedTypeRef(sb.sg.tdg))
		isSettlingScope = isSettlingScope || statementScope.GetIsSettlingScope()

		if statementScope.GetIsTerminatingStatement() {
			skipRemaining = true
		}
	}

	// If this statement block is the implementation of a member or property getter, check its return
	// type.
	if isValid {
		if labelSet.HasLabel(proto.ScopeLabel_GENERATOR_STATEMENT) {
			if !returnedType.IsVoid() {
				sb.decorateWithError(node, "Cannot return a type under a generator")
				return newScope().Invalid().WithLabelSet(labelSet).GetScope()
			}
		} else {
			parentDef, hasParent := node.StartQuery().In(parser.NodePredicateBody).TryGetNode()
			if hasParent {
				returnTypeExpected, hasReturnType := sb.sg.tdg.LookupReturnType(parentDef)
				if hasReturnType {
					// If the return type expected is void, ensure no branch returned any values.
					if returnTypeExpected.IsVoid() {
						if returnedType.IsVoid() {
							return newScope().Valid().Returning(returnedType, isSettlingScope).WithLabelSet(labelSet).GetScope()
						} else {
							sb.decorateWithError(node, "No return value expected here, found value of type '%v'", returnedType)
							return newScope().Invalid().Returning(returnedType, isSettlingScope).WithLabelSet(labelSet).GetScope()
						}
					}

					if !isSettlingScope {
						sb.decorateWithError(node, "Expected return value of type '%v' but not all paths return a value", returnTypeExpected)
						return newScope().Invalid().Returning(returnedType, isSettlingScope).WithLabelSet(labelSet).GetScope()
					}

					// Otherwise, check that the returned type matches that expected.
					if !returnedType.IsVoid() {
						rerr := returnedType.CheckSubTypeOf(returnTypeExpected)
						if rerr != nil {
							sb.decorateWithError(node, "Expected return value of type '%v': %v", returnTypeExpected, rerr)
							return newScope().Invalid().Returning(returnedType, isSettlingScope).WithLabelSet(labelSet).GetScope()
						}
					}
				}
			}
		}
	}

	// No valid return type expected.
	return newScope().IsValid(isValid).Returning(returnedType, isSettlingScope).WithLabelSet(labelSet).GetScope()
}
Beispiel #26
0
// buildJumpingCaseStatement builds the CodeDOM for a statement which jumps (branches) based on
// various cases.
func (db *domBuilder) buildJumpingCaseStatement(node compilergraph.GraphNode,
	casePredicate compilergraph.Predicate,
	caseStatementPredicate compilergraph.Predicate,
	caseExpressionPredicate compilergraph.Predicate,
	startStatement codedom.Statement,
	getCheckExpression checkExpressionGenerator) (codedom.Statement, codedom.Statement) {

	// A branching statement is an extended version of a conditional statement. For each branch, we check if the
	// branch's value matches the conditional (or "true" if there is no conditional expr). On true, the block
	// under the case is executed, followed by a jump to the final statement. Otherwise, the next block
	// in the chain is executed.
	finalStatement := codedom.EmptyStatement(node)

	// Save a break statement reference for the generated statement.
	db.breakStatementMap[node.NodeId] = finalStatement

	// Generate the statements and check expressions for each of the cases.
	var branchJumps = make([]*codedom.ConditionalJumpNode, 0)

	cit := node.StartQuery().
		Out(casePredicate).
		BuildNodeIterator()

	for cit.Next() {
		caseNode := cit.Node()
		caseStart, caseEnd := db.getStatements(caseNode, caseStatementPredicate)
		caseExpressionNode, hasCaseExpression := caseNode.TryGetNode(caseExpressionPredicate)

		// Generate the expression against which we should check. If there is no expression on
		// the case, then this is the default and we compare against "true".
		var branchExpression codedom.Expression = nil
		if hasCaseExpression {
			branchExpression = getCheckExpression(caseExpressionNode)
		} else {
			branchExpression = codedom.NominalWrapping(
				codedom.LiteralValue("true", caseNode),
				db.scopegraph.TypeGraph().BoolType(),
				caseNode)
		}

		branchJump := codedom.BranchOn(branchExpression, caseNode)
		branchJump.True = caseStart
		branchJumps = append(branchJumps, branchJump)

		// Jump the end statement, once complete, to the final statement.
		codedom.AssignNextStatement(caseEnd, codedom.UnconditionalJump(finalStatement, caseNode))
	}

	// Generate a "trampoline" that checks each case.
	for index, branchJump := range branchJumps {
		// Jump the current branch (on false) to the next conditional check.
		if index < len(branchJumps)-1 {
			branchJump.False = branchJumps[index+1]
		} else {
			branchJump.False = finalStatement
		}
	}

	// Have the start state jump to the first branch (if any).
	if len(branchJumps) > 0 {
		codedom.AssignNextStatement(startStatement, branchJumps[0])
	}

	return startStatement, finalStatement
}
Beispiel #27
0
// inferLambdaParameterTypes performs type inference to determine the types of the parameters of the
// given lambda expression (if necessary).
//
// Forms supported for inference:
//
// var someVar = (a, b) => someExpr
// someVar(1, 2)
//
// var<function<void>(...)> = (a, b) => someExpr
//
// ((a, b) => someExpr)(1, 2)
func (sb *scopeBuilder) inferLambdaParameterTypes(node compilergraph.GraphNode, context scopeContext) {
	// If the lambda has no inferred parameters, nothing more to do.
	if _, ok := node.TryGetNode(parser.NodeLambdaExpressionInferredParameter); !ok {
		return
	}

	// Otherwise, collect the names and positions of the inferred parameters.
	pit := node.StartQuery().
		Out(parser.NodeLambdaExpressionInferredParameter).
		BuildNodeIterator()

	var inferenceParameters = make([]compilergraph.GraphNode, 0)
	for pit.Next() {
		inferenceParameters = append(inferenceParameters, pit.Node())
	}

	getInferredTypes := func() ([]typegraph.TypeReference, bool) {
		// Check if the lambda expression is under a function call expression. If so, we use the types of
		// the parameters.
		parentCall, hasParentCall := node.TryGetIncomingNode(parser.NodeFunctionCallExpressionChildExpr)
		if hasParentCall {
			return sb.getFunctionCallArgumentTypes(parentCall, context), true
		}

		// Check if the lambda expression is under a variable declaration. If so, we try to infer from
		// either its declared type or its use(s).
		parentVariable, hasParentVariable := node.TryGetIncomingNode(parser.NodeVariableStatementExpression)
		if !hasParentVariable {
			return make([]typegraph.TypeReference, 0), false
		}

		// Check if the parent variable has a declared type of function. If so, then we simply
		// use the declared parameter types.
		declaredType, hasDeclaredType := sb.getDeclaredVariableType(parentVariable)
		if hasDeclaredType && declaredType.IsDirectReferenceTo(sb.sg.tdg.FunctionType()) {
			return declaredType.Parameters(), true
		}

		// Otherwise, we find all references of the variable under the parent scope that are,
		// themselves, under a function call, and intersect the types of arguments found.
		parentVariableName := parentVariable.Get(parser.NodeVariableStatementName)
		parentBlock, hasParentBlock := parentVariable.TryGetIncomingNode(parser.NodeStatementBlockStatement)
		if !hasParentBlock {
			return make([]typegraph.TypeReference, 0), false
		}

		var inferredTypes = make([]typegraph.TypeReference, 0)
		rit := sb.sg.srg.FindReferencesInScope(parentVariableName, parentBlock)
		for rit.Next() {
			funcCall, hasFuncCall := rit.Node().TryGetIncomingNode(parser.NodeFunctionCallExpressionChildExpr)
			if !hasFuncCall {
				continue
			}

			inferredTypes = sb.sg.tdg.IntersectTypes(inferredTypes, sb.getFunctionCallArgumentTypes(funcCall, context))
		}

		return inferredTypes, true
	}

	// Resolve the inferred types and decorate the parameters with them (if any).
	inferredTypes, hasInferredTypes := getInferredTypes()
	if hasInferredTypes {
		for index, inferenceParameter := range inferenceParameters {
			var inferredType = sb.sg.tdg.AnyTypeReference()
			if index < len(inferredTypes) {
				if !inferredTypes[index].IsVoid() {
					inferredType = inferredTypes[index]
				}
			}

			sb.inferredParameterTypes.Set(string(inferenceParameter.NodeId), inferredType)
			sb.modifier.Modify(inferenceParameter).DecorateWithTagged(NodePredicateInferredType, inferredType)
		}
	} else {
		for _, inferenceParameter := range inferenceParameters {
			sb.inferredParameterTypes.Set(string(inferenceParameter.NodeId), sb.sg.tdg.AnyTypeReference())
			sb.modifier.Modify(inferenceParameter).DecorateWithTagged(NodePredicateInferredType, sb.sg.tdg.AnyTypeReference())
		}
	}
}
Beispiel #28
0
// buildSmlExpression builds the CodeDOM for a SML expression.
func (db *domBuilder) buildSmlExpression(node compilergraph.GraphNode) codedom.Expression {
	smlScope, _ := db.scopegraph.GetScope(node)

	funcOrTypeRefNode := node.GetNode(parser.NodeSmlExpressionTypeOrFunction)
	funcOrTypeRefScope, _ := db.scopegraph.GetScope(funcOrTypeRefNode)

	// Build the expression for the function or constructor to be called to construct the expression.
	var declarationFunction = db.buildExpression(funcOrTypeRefNode)
	var declarationFunctionType = db.scopegraph.TypeGraph().AnyTypeReference()

	if smlScope.HasLabel(proto.ScopeLabel_SML_CONSTRUCTOR) {
		constructor, _ := funcOrTypeRefScope.StaticTypeRef(db.scopegraph.TypeGraph()).ResolveMember("Declare", typegraph.MemberResolutionStatic)
		declarationFunctionType = constructor.MemberType()
		declarationFunction = codedom.MemberReference(declarationFunction, constructor, node)
	} else {
		declarationFunctionType = funcOrTypeRefScope.ResolvedTypeRef(db.scopegraph.TypeGraph())
	}

	var declarationArguments = make([]codedom.Expression, 0)
	declFunctionParams := declarationFunctionType.Parameters()

	// If the SML expression expects any attributes, then construct the props struct, class or mapping.
	if len(declFunctionParams) > 0 {
		attributeExpressions := map[string]codedom.Expression{}

		ait := node.StartQuery().
			Out(parser.NodeSmlExpressionAttribute).
			BuildNodeIterator()

		for ait.Next() {
			attributeNode := ait.Node()
			attributeName := attributeNode.Get(parser.NodeSmlAttributeName)
			attributeExpressions[attributeName] = db.getExpressionOrDefault(
				attributeNode,
				parser.NodeSmlAttributeValue,
				codedom.NominalWrapping(
					codedom.LiteralValue("true", attributeNode),
					db.scopegraph.TypeGraph().BoolType(),
					attributeNode))
		}

		// Construct the props object expression, either as a struct, class or as a mapping.
		propsType := declFunctionParams[0]
		if smlScope.HasLabel(proto.ScopeLabel_SML_PROPS_MAPPING) {
			declarationArguments = append(declarationArguments, db.buildMappingInitializerExpression(propsType, attributeExpressions, node))
		} else {
			declarationArguments = append(declarationArguments, db.buildStructInitializerExpression(propsType, attributeExpressions, node))
		}
	}

	// If the SML expression expects any children, then construct the children stream or value (if any).
	if len(declFunctionParams) > 1 {
		cit := node.StartQuery().
			Out(parser.NodeSmlExpressionChild).
			BuildNodeIterator()

		children := db.buildExpressions(cit, buildExprNormally)

		switch {
		case smlScope.HasLabel(proto.ScopeLabel_SML_SINGLE_CHILD):
			// If there is a child, it is a value. If missing, the child is nullable, so we don't put
			// anything there.
			if len(children) > 0 {
				declarationArguments = append(declarationArguments, children[0])
			}

		case smlScope.HasLabel(proto.ScopeLabel_SML_STREAM_CHILD):
			// Single child which is a stream. Passed directly to the declaring function as an argument.
			declarationArguments = append(declarationArguments, children[0])

		case smlScope.HasLabel(proto.ScopeLabel_SML_CHILDREN):
			// The argument is a stream of the child expressions. Build it as a generator.
			if len(children) == 0 {
				// Add an empty generator.
				declarationArguments = append(declarationArguments,
					codedom.RuntimeFunctionCall(codedom.EmptyGeneratorDirect, []codedom.Expression{}, node))
			} else {
				yielders := make([]codedom.Statement, len(children))
				for index, child := range children {
					yielders[index] = codedom.YieldValue(child, child.BasisNode())
					if index > 0 {
						yielders[index-1].(codedom.HasNextStatement).SetNext(yielders[index])
					}
				}

				generatorFunc := codedom.FunctionDefinition([]string{}, []string{}, yielders[0], false, codedom.GeneratorFunction, node)
				generatorExpr := codedom.FunctionCall(generatorFunc, []codedom.Expression{}, node)
				declarationArguments = append(declarationArguments, codedom.AwaitPromise(generatorExpr, node))
			}
		default:
			panic("Unknown SML children scope label")
		}
	}

	// The value of declaration function invoked with the arguments is the initial definition.
	var definition = codedom.AwaitPromise(
		codedom.FunctionCall(declarationFunction, declarationArguments, node), node)

	// Add any decorators as wrapping around the definition call.
	dit := node.StartQuery().
		Out(parser.NodeSmlExpressionDecorator).
		BuildNodeIterator()

	for dit.Next() {
		decoratorNode := dit.Node()
		decoratorFunc := db.getExpression(decoratorNode, parser.NodeSmlDecoratorPath)

		// The value for the decorator is either the value expression given or the literal
		// value "true" if none specified.
		decoratorValue := db.getExpressionOrDefault(
			decoratorNode,
			parser.NodeSmlDecoratorValue,
			codedom.NominalWrapping(
				codedom.LiteralValue("true", decoratorNode),
				db.scopegraph.TypeGraph().BoolType(),
				decoratorNode))

		// The decorator is invoked over the definition.
		decoratorArgs := []codedom.Expression{definition, decoratorValue}
		definition = codedom.AwaitPromise(
			codedom.FunctionCall(decoratorFunc, decoratorArgs, decoratorNode),
			decoratorNode)
	}

	return definition
}
Beispiel #29
0
// scopeGenericSpecifierExpression scopes a generic specifier in the SRG.
func (sb *scopeBuilder) scopeGenericSpecifierExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope the child expression.
	childScope := sb.getScope(node.GetNode(parser.NodeGenericSpecifierChildExpr), context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	if childScope.GetKind() != proto.ScopeKind_GENERIC {
		sb.decorateWithError(node, "Cannot apply generics to non-generic scope")
		return newScope().Invalid().GetScope()
	}

	// Retrieve the underlying named scope.
	namedScope, isNamedScope := sb.getNamedScopeForScope(childScope)
	if !isNamedScope {
		panic("Generic non-named scope")
	}

	var genericType = sb.sg.tdg.VoidTypeReference()
	if namedScope.IsStatic() {
		genericType = namedScope.StaticType(context)
	} else {
		genericType = childScope.GenericTypeRef(sb.sg.tdg)
	}

	genericsToReplace := namedScope.Generics()
	if len(genericsToReplace) == 0 {
		sb.decorateWithError(node, "Cannot apply generics to non-generic type %v", genericType)
		return newScope().Invalid().GetScope()
	}

	git := node.StartQuery().
		Out(parser.NodeGenericSpecifierType).
		BuildNodeIterator()

	var genericIndex = 0
	for git.Next() {
		// Ensure we haven't gone outside the required number of generics.
		if genericIndex >= len(genericsToReplace) {
			genericIndex++
			continue
		}

		// Build the type to use in place of the generic.
		replacementType, gerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(git.Node()))
		if gerr != nil {
			sb.decorateWithError(node, "Error on type #%v in generic specifier: %v", gerr, genericIndex+1)
			return newScope().Invalid().GetScope()
		}

		// Ensure that the type meets the generic constraint.
		toReplace := genericsToReplace[genericIndex]
		if serr := replacementType.CheckSubTypeOf(toReplace.Constraint()); serr != nil {
			sb.decorateWithError(node, "Cannot use type %v as generic %v (#%v) over %v %v: %v", replacementType, toReplace.Name(), genericIndex+1, namedScope.Title(), namedScope.Name(), serr)
			return newScope().Invalid().GetScope()
		}

		// If the parent type is structural, ensure the constraint is structural.
		if genericType.IsStructurual() {
			if serr := replacementType.EnsureStructural(); serr != nil {
				sb.decorateWithError(node, "Cannot use type %v as generic %v (#%v) over %v %v: %v", replacementType, toReplace.Name(), genericIndex+1, namedScope.Title(), namedScope.Name(), serr)
				return newScope().Invalid().GetScope()
			}
		}

		// Replace the generic with the associated type.
		genericType = genericType.ReplaceType(toReplace.AsType(), replacementType)
		genericIndex = genericIndex + 1
	}

	if genericIndex != len(genericsToReplace) {
		sb.decorateWithError(node, "Generic count must match. Found: %v, expected: %v on %v %v", genericIndex, len(genericsToReplace), namedScope.Title(), namedScope.Name())
		return newScope().Invalid().GetScope()
	}

	// Save the updated type.
	if namedScope.IsStatic() {
		return newScope().
			Valid().
			ForNamedScope(namedScope, context).
			WithStaticType(genericType).
			WithKind(proto.ScopeKind_STATIC).
			GetScope()
	} else {
		return newScope().
			Valid().
			ForNamedScope(namedScope, context).
			Resolving(genericType).
			WithKind(proto.ScopeKind_VALUE).
			GetScope()
	}
}
Beispiel #30
0
// scopeSmlExpression scopes an SML expression in the SRG.
func (sb *scopeBuilder) scopeSmlExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope the type or function and ensure it refers to a "declarable" type or function.
	// A type is "declarable" if it has a constructor named Declare with a "declarable" function type signature.
	//
	// A function is "declarable" if it has the following properties:
	//  - Must return a non-void type
	//  - Parameter #1 represents the properties (attributes) and must be:
	//     1) A struct
	//     2) A class with at least one field
	//     3) A mapping
	//
	//  - Parameter #2 (optional) represents the children (contents).
	typeOrFuncScope := sb.getScope(node.GetNode(parser.NodeSmlExpressionTypeOrFunction), context)
	if !typeOrFuncScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	var functionType = sb.sg.tdg.AnyTypeReference()
	var declarationLabel = proto.ScopeLabel_SML_FUNCTION
	var childrenLabel = proto.ScopeLabel_SML_NO_CHILDREN
	var propsLabel = proto.ScopeLabel_SML_PROPS_MAPPING

	switch typeOrFuncScope.GetKind() {
	case proto.ScopeKind_VALUE:
		// Function.
		functionType = typeOrFuncScope.ResolvedTypeRef(sb.sg.tdg)
		declarationLabel = proto.ScopeLabel_SML_FUNCTION

		calledFunctionScope, _ := sb.getNamedScopeForScope(typeOrFuncScope)
		context.staticDependencyCollector.registerNamedDependency(calledFunctionScope)

	case proto.ScopeKind_STATIC:
		// Type. Ensure it has a Declare constructor.
		module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
		staticType := typeOrFuncScope.StaticTypeRef(sb.sg.tdg)

		declareConstructor, rerr := staticType.ResolveAccessibleMember("Declare", module, typegraph.MemberResolutionStatic)
		if rerr != nil {
			sb.decorateWithError(node, "A type used in a SML declaration tag must have a 'Declare' constructor: %v", rerr)
			return newScope().Invalid().GetScope()
		}

		context.staticDependencyCollector.registerDependency(declareConstructor)

		functionType = declareConstructor.MemberType()
		declarationLabel = proto.ScopeLabel_SML_CONSTRUCTOR

	case proto.ScopeKind_GENERIC:
		sb.decorateWithError(node, "A generic type cannot be used in a SML declaration tag")
		return newScope().Invalid().GetScope()

	default:
		panic("Unknown scope kind")
	}

	// Ensure the function type is a function.
	if !functionType.IsDirectReferenceTo(sb.sg.tdg.FunctionType()) {
		sb.decorateWithError(node, "Declared reference in an SML declaration tag must be a function. Found: %v", functionType)
		return newScope().Invalid().GetScope()
	}

	// Ensure the function doesn't return void.
	declaredType := functionType.Generics()[0]
	if declaredType.IsVoid() {
		sb.decorateWithError(node, "Declarable function used in an SML declaration tag cannot return void")
		return newScope().Invalid().GetScope()
	}

	parameters := functionType.Parameters()

	// Check for attributes.
	var isValid = true
	var resolvedType = declaredType
	if _, ok := node.TryGetNode(parser.NodeSmlExpressionAttribute); ok || len(parameters) >= 1 {
		if len(parameters) < 1 {
			sb.decorateWithError(node, "Declarable function or constructor used in an SML declaration tag with attributes must have a 'props' parameter as parameter #1. Found: %v", functionType)
			return newScope().Invalid().Resolving(declaredType).GetScope()
		}

		propsType := parameters[0]

		// Ensure that the first parameter is either structural, a class with ForProps or a Mapping.
		if propsType.NullValueAllowed() {
			sb.decorateWithError(node, "Props parameter (parameter #1) of a declarable function or constructor used in an SML declaration tag cannot allow null values. Found: %v", propsType)
			return newScope().Invalid().Resolving(declaredType).GetScope()
		}

		switch {
		case propsType.IsDirectReferenceTo(sb.sg.tdg.MappingType()):
			// Mappings are always allowed.
			propsLabel = proto.ScopeLabel_SML_PROPS_MAPPING

		case propsType.IsRefToStruct():
			// Structs are always allowed.
			propsLabel = proto.ScopeLabel_SML_PROPS_STRUCT

		case propsType.IsRefToClass():
			// Classes are allowed if they have at least one field.
			if len(propsType.ReferredType().Fields()) == 0 {
				sb.decorateWithError(node, "Props parameter (parameter #1) of a declarable function or constructor used in an SML declaration tag has type %v, which does not have any settable fields; use an empty `struct` instead if this is the intended behavior", propsType)
				return newScope().Invalid().Resolving(declaredType).GetScope()
			}

			propsLabel = proto.ScopeLabel_SML_PROPS_CLASS

		default:
			// Otherwise, the type is not valid.
			sb.decorateWithError(node, "Props parameter (parameter #1) of a declarable function or constructor used in an SML declaration tag must be a struct, a class with a ForProps constructor or a Mapping. Found: %v", propsType)
			return newScope().Invalid().Resolving(declaredType).GetScope()
		}

		// At this point we know we have a declarable function used in the tag.
		// Scope the attributes to match the props type.
		ait := node.StartQuery().
			Out(parser.NodeSmlExpressionAttribute).
			BuildNodeIterator()

		attributesEncountered := map[string]bool{}

		for ait.Next() {
			attributeName, ok := sb.scopeSmlAttribute(ait.Node(), propsType, context)
			attributesEncountered[attributeName] = true
			isValid = isValid && ok
		}

		// If the props type is not a mapping, ensure that all required fields were set.
		if !propsType.IsDirectReferenceTo(sb.sg.tdg.MappingType()) {
			for _, requiredField := range propsType.ReferredType().RequiredFields() {
				if _, ok := attributesEncountered[requiredField.Name()]; !ok {
					sb.decorateWithError(node, "Required attribute '%v' is missing for SML declaration props type %v", requiredField.Name(), propsType)
					isValid = false
				}
			}
		}
	}

	// Scope decorators.
	dit := node.StartQuery().
		Out(parser.NodeSmlExpressionDecorator).
		BuildNodeIterator()

	for dit.Next() {
		decoratorReturnType, ok := sb.scopeSmlDecorator(dit.Node(), resolvedType, context)
		resolvedType = decoratorReturnType
		isValid = isValid && ok
	}

	// Scope the children to match the childs type.
	if _, ok := node.TryGetNode(parser.NodeSmlExpressionChild); ok || len(parameters) >= 2 {
		if len(parameters) < 2 {
			sb.decorateWithError(node, "Declarable function or constructor used in an SML declaration tag with children must have a 'children' parameter. Found: %v", functionType)
			return newScope().Invalid().Resolving(resolvedType).GetScope()
		}

		childsType := parameters[1]

		// Scope and collect the types of all the children.
		var childrenTypes = make([]typegraph.TypeReference, 0)

		cit := node.StartQuery().
			Out(parser.NodeSmlExpressionChild).
			BuildNodeIterator()

		for cit.Next() {
			childNode := cit.Node()
			childScope := sb.getScope(childNode, context)
			if !childScope.GetIsValid() {
				isValid = false
			}

			childrenTypes = append(childrenTypes, childScope.ResolvedTypeRef(sb.sg.tdg))
		}

		// Check if the children parameter is a stream. If so, we match the stream type. Otherwise,
		// we check if the child matches the value expected.
		if childsType.IsDirectReferenceTo(sb.sg.tdg.StreamType()) {
			// If there is a single child that is also a matching stream, then we allow it.
			if len(childrenTypes) == 1 && childrenTypes[0] == childsType {
				childrenLabel = proto.ScopeLabel_SML_STREAM_CHILD
			} else {
				childrenLabel = proto.ScopeLabel_SML_CHILDREN
				streamValueType := childsType.Generics()[0]

				// Ensure the child types match.
				for index, childType := range childrenTypes {
					if serr := childType.CheckSubTypeOf(streamValueType); serr != nil {
						sb.decorateWithError(node, "Child #%v under SML declaration must be subtype of %v: %v", index+1, streamValueType, serr)
						isValid = false
					}
				}
			}
		} else if len(childrenTypes) > 1 {
			// Since not a slice, it can only be one or zero children.
			sb.decorateWithError(node, "SML declaration tag allows at most a single child. Found: %v", len(childrenTypes))
			return newScope().Invalid().Resolving(resolvedType).GetScope()
		} else {
			childrenLabel = proto.ScopeLabel_SML_SINGLE_CHILD

			// If the child type is not nullable, then we need to make sure a value was specified.
			if !childsType.NullValueAllowed() && len(childrenTypes) < 1 {
				sb.decorateWithError(node, "SML declaration tag requires a single child. Found: %v", len(childrenTypes))
				return newScope().Invalid().Resolving(resolvedType).GetScope()
			}

			// Ensure the child type matches.
			if len(childrenTypes) == 1 {
				if serr := childrenTypes[0].CheckSubTypeOf(childsType); serr != nil {
					sb.decorateWithError(node, "SML declaration tag requires a child of type %v: %v", childsType, serr)
					return newScope().Invalid().Resolving(resolvedType).GetScope()
				}
			}
		}
	}

	return newScope().
		IsValid(isValid).
		Resolving(resolvedType).
		WithLabel(declarationLabel).
		WithLabel(propsLabel).
		WithLabel(childrenLabel).
		GetScope()
}