// 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) }
// 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) }
// 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) }
// 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) }
// 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) }
// 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) }
// 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) }
// 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) }
// 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) }
// 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) }
// 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) }