// FindReferencesInScope finds all identifier expressions that refer to the given name, under the given // scope. func (g *SRG) FindReferencesInScope(name string, node compilergraph.GraphNode) compilergraph.NodeIterator { // Note: This filter ensures that the name is accessible in the scope of the given node by checking that // the node referencing the name is contained by the given node. containingFilter := func(q compilergraph.GraphQuery) compilergraph.Query { startRune := node.GetValue(parser.NodePredicateStartRune).Int() endRune := node.GetValue(parser.NodePredicateEndRune).Int() return q. HasWhere(parser.NodePredicateStartRune, compilergraph.WhereGT, startRune). HasWhere(parser.NodePredicateEndRune, compilergraph.WhereLT, endRune) } return g.layer.StartQuery(name). In(parser.NodeIdentifierExpressionName). IsKind(parser.NodeTypeIdentifierExpression). Has(parser.NodePredicateSource, node.Get(parser.NodePredicateSource)). FilterBy(containingFilter). BuildNodeIterator() }
// OutOfScope returns any resources that will be out of scope when context changes to the given reference // node. Used to determine which resources need to be popped off of the stack when jumps occurr. Note that // the reference node *must be an SRG node*. func (s *ResourceStack) OutOfScope(referenceNode compilergraph.GraphNode) []resource { resources := make([]resource, 0) // Find the start and end location for the reference node. referenceStart := referenceNode.GetValue(parser.NodePredicateStartRune).Int() referenceEnd := referenceNode.GetValue(parser.NodePredicateEndRune).Int() var current = s.top for index := 0; index < s.size; index++ { currentResource := current.value // Find the start and end location for the resource node. resourceStart := currentResource.basis.GetValue(parser.NodePredicateStartRune).Int() resourceEnd := currentResource.basis.GetValue(parser.NodePredicateEndRune).Int() if !(referenceStart >= resourceStart && referenceEnd <= resourceEnd) { resources = append(resources, currentResource) } current = current.next } return resources }
// salForNode returns a SourceAndLocation for the given graph node. func salForNode(node compilergraph.GraphNode) compilercommon.SourceAndLocation { return compilercommon.NewSourceAndLocation( compilercommon.InputSource(node.Get(parser.NodePredicateSource)), node.GetValue(parser.NodePredicateStartRune).Int()) }
// findAddedNameInScope finds the {parameter, with, loop, var} node exposing the given name, if any. func (g *SRG) findAddedNameInScope(name string, node compilergraph.GraphNode) (compilergraph.GraphNode, bool) { nodeSource := node.Get(parser.NodePredicateSource) nodeStartIndex := node.GetValue(parser.NodePredicateStartRune).Int() // Note: This filter ensures that the name is accessible in the scope of the given node by checking that // the node adding the name contains the given node. containingFilter := func(q compilergraph.GraphQuery) compilergraph.Query { startRune := node.GetValue(parser.NodePredicateStartRune).Int() endRune := node.GetValue(parser.NodePredicateEndRune).Int() return q. In(parser.NodePredicateTypeMemberParameter, parser.NodeLambdaExpressionInferredParameter, parser.NodeLambdaExpressionParameter, parser.NodePredicateTypeMemberGeneric, parser.NodeStatementNamedValue, parser.NodeAssignedDestination, parser.NodeAssignedRejection, parser.NodePredicateChild, parser.NodeStatementBlockStatement). InIfKind(parser.NodeStatementBlockStatement, parser.NodeTypeResolveStatement). HasWhere(parser.NodePredicateStartRune, compilergraph.WhereLTE, startRune). HasWhere(parser.NodePredicateEndRune, compilergraph.WhereGTE, endRune) } nit := g.layer.StartQuery(name). In("named"). Has(parser.NodePredicateSource, nodeSource). IsKind(parser.NodeTypeParameter, parser.NodeTypeNamedValue, parser.NodeTypeAssignedValue, parser.NodeTypeVariableStatement, parser.NodeTypeLambdaParameter, parser.NodeTypeGeneric). FilterBy(containingFilter). BuildNodeIterator(parser.NodePredicateStartRune, parser.NodePredicateEndRune) // Sort the nodes found by location and choose the closest node. var results = make(scopeResultNodes, 0) for nit.Next() { node := nit.Node() startIndex := nit.GetPredicate(parser.NodePredicateStartRune).Int() // If the node is a variable statement or assigned value, we have do to additional checks // (since they are not block scoped but rather statement scoped). if node.Kind() == parser.NodeTypeVariableStatement || node.Kind() == parser.NodeTypeAssignedValue { endIndex := nit.GetPredicate(parser.NodePredicateEndRune).Int() if node.Kind() == parser.NodeTypeAssignedValue { if parentNode, ok := node.TryGetIncomingNode(parser.NodeAssignedDestination); ok { endIndex = parentNode.GetValue(parser.NodePredicateEndRune).Int() } else if parentNode, ok := node.TryGetIncomingNode(parser.NodeAssignedRejection); ok { endIndex = parentNode.GetValue(parser.NodePredicateEndRune).Int() } else { panic("Missing assigned parent") } } // Check that the startIndex of the variable statement is <= the startIndex of the parent node if startIndex > nodeStartIndex { continue } // Ensure that the scope starts after the end index of the variable. Otherwise, the variable // name could be used in its initializer expression (which is expressly disallowed). if nodeStartIndex <= endIndex { continue } } results = append(results, scopeResultNode{node, startIndex}) } if len(results) == 1 { // If there is a single result, return it. return results[0].node, true } else if len(results) > 1 { // Otherwise, sort the list by startIndex and choose the one closest to the scope node. sort.Sort(results) return results[0].node, true } return compilergraph.GraphNode{}, false }
// GetUniqueId returns a unique hash ID for the SRG node that is stable across compilations. func GetUniqueId(srgNode compilergraph.GraphNode) string { hashBytes := []byte(srgNode.Get(parser.NodePredicateSource) + ":" + strconv.Itoa(srgNode.GetValue(parser.NodePredicateStartRune).Int())) sha256bytes := sha256.Sum256(hashBytes) return hex.EncodeToString(sha256bytes[:])[0:8] }