// buildArrowStatement builds the CodeDOM for an arrow statement. func (db *domBuilder) buildArrowStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) { sourceExpr := codedom.RuntimeFunctionCall( codedom.TranslatePromiseFunction, []codedom.Expression{db.getExpression(node, parser.NodeArrowStatementSource)}, node) destinationNode := node.GetNode(parser.NodeArrowStatementDestination) destinationScope, _ := db.scopegraph.GetScope(destinationNode) var destinationTarget codedom.Expression = nil var rejectionTarget codedom.Expression = nil // Retrieve the expression of the destination variable. if !destinationScope.GetIsAnonymousReference() { destinationTarget = db.buildAssignmentExpression(destinationNode, codedom.LocalReference("resolved", node), node) } // Retrieve the expression of the rejection variable. rejectionNode, hasRejection := node.TryGetNode(parser.NodeArrowStatementRejection) if hasRejection { rejectionScope, _ := db.scopegraph.GetScope(rejectionNode) if !rejectionScope.GetIsAnonymousReference() { rejectionTarget = db.buildAssignmentExpression(rejectionNode, codedom.LocalReference("rejected", node), node) } } empty := codedom.EmptyStatement(node) promise := codedom.ArrowPromise(sourceExpr, destinationTarget, rejectionTarget, empty, node) return promise, empty }
// buildInitializationCompoundExpression builds the compound expression for calling the specified initializers on a struct. func (db *domBuilder) buildInitializationCompoundExpression(structType typegraph.TypeReference, initializers map[string]codedom.Expression, structValue codedom.Expression, node compilergraph.GraphNode) codedom.Expression { // Create a variable to hold the struct instance. structInstanceVarName := db.generateScopeVarName(node) // Build the assignment expressions. assignmentExpressions := make([]codedom.Expression, len(initializers)) var index = 0 for fieldName, initializer := range initializers { member, found := structType.ResolveMember(fieldName, typegraph.MemberResolutionInstance) if !found { panic("Member not found in struct initializer construction") } assignmentExpressions[index] = codedom.MemberAssignment(member, codedom.MemberReference( codedom.LocalReference(structInstanceVarName, node), member, node), initializer, node) index = index + 1 } // Return a compound expression that takes in the struct value and the assignment expressions, // executes them, and returns the struct value. return codedom.CompoundExpression(structInstanceVarName, structValue, assignmentExpressions, codedom.LocalReference(structInstanceVarName, node), 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) }
// generateWithShortCircuiting generates an expression with automatic short circuiting based on the value found // in the resultName variable and compared to the given compare value. func (eg *expressionGenerator) generateWithShortCircuiting(expr codedom.Expression, resultName string, compareValue codedom.Expression, context generationContext) esbuilder.ExpressionBuilder { shortCircuiter := eg.generateExpression( codedom.RuntimeFunctionCall(codedom.ShortCircuitPromiseFunction, []codedom.Expression{codedom.LocalReference(resultName, expr.BasisNode()), compareValue}, expr.BasisNode()), context) return eg.generateExpression(expr, generationContext{shortCircuiter}) }
// pushResource adds a resource to the resource stack with the given name. func (sg *stateGenerator) pushResource(name string, basis compilergraph.GraphNode) { sg.managesResources = true // pushr(varName, 'varName'); pushCall := codedom.RuntimeFunctionCall( codedom.StatePushResourceFunction, []codedom.Expression{ codedom.LocalReference(name, basis), codedom.LiteralValue("'"+name+"'", basis), }, basis, ) sg.generateStates(codedom.ExpressionStatement(pushCall, basis), generateImplicitState) sg.resources.Push(resource{ name: name, basis: basis, }) }
// buildNamedAccess builds the CodeDOM for a member access expression. func (db *domBuilder) buildNamedAccess(node compilergraph.GraphNode, name string, childExprNode *compilergraph.GraphNode) codedom.Expression { scope, _ := db.scopegraph.GetScope(node) namedReference, hasNamedReference := db.scopegraph.GetReferencedName(scope) // Reference to an unknown name or a nullable member access must be a dynamic access. if !hasNamedReference || node.Kind() == parser.NodeNullableMemberAccessExpression { return codedom.DynamicAccess(db.buildExpression(*childExprNode), name, node) } // Reference to a local name is a var or parameter. if namedReference.IsLocal() { return codedom.LocalReference(namedReference.Name(), node) } // Check for a reference to a type. if typeRef, isType := namedReference.Type(); isType { return codedom.StaticTypeReference(typeRef, node) } // Check for a reference to a member. if memberRef, isMember := namedReference.Member(); isMember { // If the member is under a child expression, build as an access expression. if childExprNode != nil { childExpr := db.buildExpression(*childExprNode) _, underFuncCall := node.TryGetIncomingNode(parser.NodeFunctionCallExpressionChildExpr) isAliasedFunctionReference := scope.ResolvedTypeRef(db.scopegraph.TypeGraph()).HasReferredType(db.scopegraph.TypeGraph().FunctionType()) if isAliasedFunctionReference && !underFuncCall { return codedom.DynamicAccess(childExpr, memberRef.Name(), node) } else { return codedom.MemberReference(childExpr, memberRef, node) } } else { // This is a direct access of a static member. Generate an access under the module. return codedom.StaticMemberReference(memberRef, db.scopegraph.TypeGraph().AnyTypeReference(), node) } } panic("Unknown kind of named access") return nil }
// buildThisLiteral builds the CodeDOM for the this literal. func (db *domBuilder) buildThisLiteral(node compilergraph.GraphNode) codedom.Expression { return codedom.LocalReference(DEFINED_THIS_PARAMETER, node) }
// buildLoopStatement builds the CodeDOM for a loop statement. func (db *domBuilder) buildLoopStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) { startStatement := codedom.EmptyStatement(node) startStatement.MarkReferenceable() finalStatement := codedom.EmptyStatement(node) // Save initial continue and break statements for the loop. db.continueStatementMap[node.NodeId] = startStatement db.breakStatementMap[node.NodeId] = finalStatement // A loop statement is buildd as a start statement which conditionally jumps to either the loop body // (on true) or, on false, jumps to a final state after the loop. if loopExpr, hasLoopExpr := db.tryGetExpression(node, parser.NodeLoopStatementExpression); hasLoopExpr { // Check for a named value under the loop. If found, this is a loop over a stream or streamable. namedValue, hasNamedValue := node.TryGetNode(parser.NodeStatementNamedValue) if hasNamedValue { namedValueName := namedValue.Get(parser.NodeNamedValueName) resultVarName := db.generateScopeVarName(node) namedValueScope, _ := db.scopegraph.GetScope(namedValue) // Create the stream variable. streamVarName := db.generateScopeVarName(node) var streamVariable = codedom.VarDefinitionWithInit(streamVarName, loopExpr, node) // If the expression is Streamable, first call .Stream() on the expression to get the stream // for the variable. if namedValueScope.HasLabel(proto.ScopeLabel_STREAMABLE_LOOP) { // Call .Stream() on the expression. streamableMember, _ := db.scopegraph.TypeGraph().StreamableType().GetMember("Stream") streamExpr := codedom.MemberCall( codedom.MemberReference(loopExpr, streamableMember, namedValue), streamableMember, []codedom.Expression{}, namedValue) streamVariable = codedom.VarDefinitionWithInit(streamVarName, streamExpr, node) } // Create variables to hold the named value (as requested in the SRG) and the loop result. namedVariable := codedom.VarDefinition(namedValueName, node) resultVariable := codedom.VarDefinition(resultVarName, node) // Create an expression statement to set the result variable to a call to Next(). streamMember, _ := db.scopegraph.TypeGraph().StreamType().GetMember("Next") nextCallExpr := codedom.MemberCall( codedom.MemberReference(codedom.LocalReference(streamVarName, node), streamMember, node), streamMember, []codedom.Expression{}, namedValue) resultExpressionStatement := codedom.ExpressionStatement(codedom.LocalAssignment(resultVarName, nextCallExpr, namedValue), namedValue) resultExpressionStatement.MarkReferenceable() // Set the continue statement to call Next() again. db.continueStatementMap[node.NodeId] = resultExpressionStatement // Create an expression statement to set the named variable to the first part of the tuple. namedExpressionStatement := codedom.ExpressionStatement( codedom.LocalAssignment(namedValueName, codedom.NativeAccess( codedom.LocalReference(resultVarName, namedValue), "First", namedValue), namedValue), namedValue) // Jump to the body state if the second part of the tuple in the result variable is true. bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock) checkJump := codedom.ConditionalJump( codedom.NativeAccess(codedom.LocalReference(resultVarName, node), "Second", node), bodyStart, finalStatement, node) // Steps: // 1) Empty statement // 2) Create the stream's variable (with no value) // 3) Create the named value variable (with no value) // 4) Create the result variable (with no value) // 5) (loop starts here) Pull the Next() result out of the stream // 6) Pull the true/false bool out of the result // 7) Pull the named value out of the result // 8) Jump based on the true/false boolean value codedom.AssignNextStatement(startStatement, streamVariable) codedom.AssignNextStatement(streamVariable, namedVariable) codedom.AssignNextStatement(namedVariable, resultVariable) codedom.AssignNextStatement(resultVariable, resultExpressionStatement) codedom.AssignNextStatement(resultExpressionStatement, namedExpressionStatement) codedom.AssignNextStatement(namedExpressionStatement, checkJump) // Jump to the result checking expression once the loop body completes. directJump := codedom.UnconditionalJump(resultExpressionStatement, node) codedom.AssignNextStatement(bodyEnd, directJump) return startStatement, finalStatement } else { bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock) // Loop over a direct boolean expression which is evaluated on each iteration. initialJump := codedom.ConditionalJump(loopExpr, bodyStart, finalStatement, node) directJump := codedom.UnconditionalJump(initialJump, node) codedom.AssignNextStatement(bodyEnd, directJump) codedom.AssignNextStatement(startStatement, initialJump) return startStatement, finalStatement } } else { bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock) // A loop without an expression just loops infinitely over the body. directJump := codedom.UnconditionalJump(bodyStart, node) codedom.AssignNextStatement(bodyEnd, directJump) codedom.AssignNextStatement(startStatement, bodyStart) return startStatement, directJump } }