// 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) }
// 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) }
// 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) }
// 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) }
// generateMemberCall generates the expression source for a call to a module or type member. func (eg *expressionGenerator) generateMemberCall(memberCall *codedom.MemberCallNode, context generationContext) esbuilder.ExpressionBuilder { if memberCall.Member.IsOperator() && memberCall.Member.IsNative() { // This is a call to a native operator. if memberCall.Member.Name() != "index" { panic("Native call to non-index operator") } refExpr := memberCall.ChildExpression.(*codedom.MemberReferenceNode).ChildExpression return eg.generateExpression(codedom.NativeIndexing(refExpr, memberCall.Arguments[0], memberCall.BasisNode()), context) } callPath := memberCall.ChildExpression arguments := memberCall.Arguments var functionCall = codedom.FunctionCall(callPath, arguments, memberCall.BasisNode()) if memberCall.Nullable { // Invoke the function with a specialized nullable-invoke. refExpr := callPath.(*codedom.DynamicAccessNode).ChildExpression var isPromising = "false" if memberCall.Member.IsPromising() { isPromising = "true" } localArguments := []codedom.Expression{ refExpr, codedom.LiteralValue("'"+memberCall.Member.Name()+"'", refExpr.BasisNode()), codedom.LiteralValue(isPromising, memberCall.BasisNode()), codedom.ArrayLiteral(arguments, memberCall.BasisNode()), } functionCall = codedom.RuntimeFunctionCall(codedom.NullableInvokeFunction, localArguments, memberCall.BasisNode()) } return eg.generateExpression(codedom.WrapIfPromising(functionCall, memberCall.Member, memberCall.BasisNode()), context) }
// 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 }