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