Example #1
0
// 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)
}
Example #2
0
// 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)
}
Example #3
0
// buildMappingInitializerExpression builds the CodeDOM for initializing a mapping literal expression.
func (db *domBuilder) buildMappingInitializerExpression(mappingType typegraph.TypeReference, initializers map[string]codedom.Expression, node compilergraph.GraphNode) codedom.Expression {
	var entries = make([]codedom.ObjectLiteralEntryNode, 0)
	for name, expr := range initializers {
		entries = append(entries,
			codedom.ObjectLiteralEntryNode{
				codedom.LiteralValue(strconv.Quote(name), expr.BasisNode()),
				expr,
				expr.BasisNode(),
			})
	}

	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)
}
Example #4
0
// 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)
}
Example #5
0
// buildStructInitializerExpression builds an initializer expression for a struct type.
func (db *domBuilder) buildStructInitializerExpression(structType typegraph.TypeReference, initializers map[string]codedom.Expression, node compilergraph.GraphNode) codedom.Expression {
	staticType := structType.ReferredType()

	var arguments = make([]codedom.Expression, 0)
	for _, field := range staticType.RequiredFields() {
		arguments = append(arguments, initializers[field.Name()])
		delete(initializers, field.Name())
	}

	constructor, found := structType.ResolveMember("new", typegraph.MemberResolutionStatic)
	if !found {
		panic(fmt.Sprintf("Missing new constructor on type %v", structType))
	}

	newCall := codedom.MemberCall(
		codedom.MemberReference(
			codedom.TypeLiteral(structType, node),
			constructor,
			node),
		constructor,
		arguments,
		node)

	// If there are no initializers, then just return the new value directly.
	if len(initializers) == 0 {
		return newCall
	}

	return db.buildInitializationCompoundExpression(structType, initializers, newCall, node)
}
Example #6
0
// buildMatchStatement builds the CodeDOM for a match statement.
func (db *domBuilder) buildMatchStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) {
	// Retrieve (or generate) the name of a variable to hold the value being matched against.
	var matchExprVarName = ""
	if namedValue, hasNamedValue := node.TryGetNode(parser.NodeStatementNamedValue); hasNamedValue {
		matchExprVarName = namedValue.Get(parser.NodeNamedValueName)
	} else {
		matchExprVarName = db.generateScopeVarName(node)
	}

	// Set the match expression's value into the variable.
	startStatement := codedom.VarDefinitionWithInit(matchExprVarName, db.getExpression(node, parser.NodeMatchStatementExpression), node)

	getCheckExpression := func(caseTypeRefNode compilergraph.GraphNode) codedom.Expression {
		caseTypeLiteral, _ := db.scopegraph.ResolveSRGTypeRef(
			db.scopegraph.SourceGraph().GetTypeRef(caseTypeRefNode))

		return codedom.NominalWrapping(
			codedom.RuntimeFunctionCall(codedom.IsTypeFunction,
				[]codedom.Expression{
					codedom.LocalReference(matchExprVarName, caseTypeRefNode),
					codedom.TypeLiteral(caseTypeLiteral, caseTypeRefNode),
				},
				caseTypeRefNode),
			db.scopegraph.TypeGraph().BoolType(),
			caseTypeRefNode)
	}

	return db.buildJumpingCaseStatement(node, parser.NodeMatchStatementCase,
		parser.NodeMatchStatementCaseStatement, parser.NodeMatchStatementCaseTypeReference,
		startStatement, getCheckExpression)
}
Example #7
0
// buildCastExpression builds the CodeDOM for a cast expression.
func (db *domBuilder) buildCastExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := db.getExpression(node, parser.NodeCastExpressionChildExpr)

	// Determine the resulting type.
	scope, _ := db.scopegraph.GetScope(node)
	resultingType := scope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	// If the resulting type is a structural subtype of the child expression's type, then
	// we are accessing the automatically composited inner instance.
	childScope, _ := db.scopegraph.GetScope(node.GetNode(parser.NodeCastExpressionChildExpr))
	childType := childScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	if childType.CheckStructuralSubtypeOf(resultingType) {
		return codedom.NestedTypeAccess(childExpr, resultingType, node)
	}

	// Otherwise, add a cast call with the cast type.
	typeLiteral := codedom.TypeLiteral(resultingType, node)
	allowNull := codedom.LiteralValue("false", node)
	if resultingType.NullValueAllowed() {
		allowNull = codedom.LiteralValue("true", node)
	}

	return codedom.RuntimeFunctionCall(codedom.CastFunction, []codedom.Expression{childExpr, typeLiteral, allowNull}, node)
}
Example #8
0
// generateNominalWrapping generates the expression source for the nominal wrapping of an instance of a base type.
func (eg *expressionGenerator) generateNominalWrapping(nominalWrapping *codedom.NominalWrappingNode, context generationContext) esbuilder.ExpressionBuilder {
	// If this is a wrap is of an unwrap, then cancel both operations if we are simply rewrapping
	// the unwrapped type.
	if unwrapping, ok := nominalWrapping.ChildExpression.(*codedom.NominalUnwrappingNode); ok {
		if unwrapping.ChildExpressionType.NominalDataType() == nominalWrapping.NominalTypeRef {
			// Skip entirely.
			return eg.generateExpression(unwrapping.ChildExpression, context)
		}
	}

	// If the child expression being wrapped is non-nullable and not a nominal or struct,
	// then we can use a fast-path box call without the extra unboxing.
	boxFunction := codedom.BoxFunction
	if !nominalWrapping.ChildExpressionType.NullValueAllowed() &&
		(!nominalWrapping.ChildExpressionType.IsNominalOrStruct() || nominalWrapping.IsLiteralWrap) {
		boxFunction = codedom.FastBoxFunction
	}

	call := codedom.RuntimeFunctionCall(
		boxFunction,
		[]codedom.Expression{
			nominalWrapping.ChildExpression,
			codedom.TypeLiteral(nominalWrapping.NominalTypeRef, nominalWrapping.BasisNode())},
		nominalWrapping.BasisNode())
	return eg.generateExpression(call, context)
}
Example #9
0
// buildLoopExpression builds the CodeDOM for a loop expression.
func (db *domBuilder) buildLoopExpression(node compilergraph.GraphNode) codedom.Expression {
	member, found := db.scopegraph.TypeGraph().StreamType().ParentModule().FindMember("MapStream")
	if !found {
		panic("Missing MapStream function under Stream's module")
	}

	// Retrieve the item type for the members of the stream and the mapped values.
	mapExpr := node.GetNode(parser.NodeLoopExpressionMapExpression)
	namedValue := node.GetNode(parser.NodeLoopExpressionNamedValue)

	namedScope, _ := db.scopegraph.GetScope(namedValue)
	namedItemType := namedScope.AssignableTypeRef(db.scopegraph.TypeGraph())

	mapScope, _ := db.scopegraph.GetScope(mapExpr)
	mappedItemType := mapScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	// Build a reference to the Map function.
	mapFunctionReference := codedom.FunctionCall(
		codedom.StaticMemberReference(member, db.scopegraph.TypeGraph().StreamTypeReference(mappedItemType), node),
		[]codedom.Expression{
			codedom.TypeLiteral(namedItemType, node),
			codedom.TypeLiteral(mappedItemType, node),
		},
		node)

	// A loop expression is replaced with a call to the Map function, with the stream as the first parameter
	// and a mapper function which resolves the mapped value as the second.
	builtMapExpr := db.buildExpression(mapExpr)
	builtStreamExpr := db.getExpression(node, parser.NodeLoopExpressionStreamExpression)

	loopValueName := namedValue.Get(parser.NodeNamedValueName)
	mapperFunction := codedom.FunctionDefinition(
		[]string{},
		[]string{loopValueName},
		codedom.Resolution(builtMapExpr, builtMapExpr.BasisNode()),
		false,
		codedom.NormalFunction,
		builtMapExpr.BasisNode())

	return codedom.AwaitPromise(
		codedom.FunctionCall(mapFunctionReference,
			[]codedom.Expression{builtStreamExpr, mapperFunction},
			node),
		node)
}
Example #10
0
// buildCollectionInitializerExpression builds a literal collection expression.
func (db *domBuilder) buildCollectionInitializerExpression(collectionType typegraph.TypeReference, valueExprs []codedom.Expression, emptyConstructorName string, arrayConstructorName string, node compilergraph.GraphNode) codedom.Expression {
	if len(valueExprs) == 0 {
		// Empty collection. Call the empty constructor directly.
		constructor, _ := collectionType.ResolveMember(emptyConstructorName, typegraph.MemberResolutionStatic)
		return codedom.MemberCall(
			codedom.MemberReference(codedom.TypeLiteral(collectionType, node), constructor, node),
			constructor,
			[]codedom.Expression{},
			node)
	}

	arrayExpr := codedom.ArrayLiteral(valueExprs, node)

	constructor, _ := collectionType.ResolveMember(arrayConstructorName, typegraph.MemberResolutionStatic)
	return codedom.MemberCall(
		codedom.MemberReference(codedom.TypeLiteral(collectionType, node), constructor, node),
		constructor,
		[]codedom.Expression{arrayExpr},
		node)
}
Example #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)
}