| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 | /* * EliasDB * * Copyright 2016 Matthias Ladkau. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */package interpreterimport (	"strings"	"devt.de/krotik/eliasdb/eql/parser"	"devt.de/krotik/eliasdb/graph/data")/*traversalRuntime is the runtime for traversals.*/type traversalRuntime struct {	rtp  *eqlRuntimeProvider	node *parser.ASTNode	where *parser.ASTNode // Traversal where clause	sourceNode data.Node   // Source node for traversal - should be injected by the parent	spec       string      // Spec for this traversal	specIndex  int         // Index of this traversal in the traversals array	nodes      []data.Node // Nodes of the last traversal result	edges      []data.Edge // Edges of the last traversal result	curptr     int         // Pointer to the next node in the last traversal result}/*traversalRuntimeInst returns a new runtime component instance.*/func traversalRuntimeInst(rtp *eqlRuntimeProvider, node *parser.ASTNode) parser.Runtime {	return &traversalRuntime{rtp, node, nil, nil, "", -1, nil, nil, 0}}/*Validate this node and all its child nodes.*/func (rt *traversalRuntime) Validate() error {	spec := rt.node.Children[0].Token.Val	rt.specIndex = -1	// Check traversal spec	sspec := strings.Split(spec, ":")	if len(sspec) != 4 {		return rt.rtp.newRuntimeError(ErrInvalidSpec, spec, rt.node)	}	rt.spec = spec	rt.specIndex = len(rt.rtp.specs)	rt.where = nil	rt.rtp.specs = append(rt.rtp.specs, spec)	rt.rtp.attrsNodes = append(rt.rtp.attrsNodes, make(map[string]string))	rt.rtp.attrsEdges = append(rt.rtp.attrsEdges, make(map[string]string))	// Go through all deeper traversals	for _, child := range rt.node.Children[1:] {		if child.Name == parser.NodeTRAVERSE {			if err := child.Runtime.Validate(); err != nil {				return err			}		} else if child.Name == parser.NodeWHERE {			whereRuntime := child.Runtime.(*whereRuntime)			whereRuntime.specIndex = rt.specIndex			// Reset state of where and store it			if err := whereRuntime.Validate(); err != nil {				return err			}			rt.where = child		} else {			return rt.rtp.newRuntimeError(ErrInvalidConstruct, child.Name, child)		}	}	return nil}/*hasMoreNodes returns true if this traversal runtime component can produce morenodes. If the result is negative then a new source node is required.*/func (rt *traversalRuntime) hasMoreNodes() bool {	for _, child := range rt.node.Children[1:] {		if child.Name == parser.NodeTRAVERSE {			childRuntime := child.Runtime.(*traversalRuntime)			if childRuntime.hasMoreNodes() {				return true			}		}	}	return rt.curptr < len(rt.nodes)}/*newSource assigns a new source node to this traversal component andtraverses it.*/func (rt *traversalRuntime) newSource(node data.Node) error {	var nodes []data.Node	var edges []data.Edge	rt.sourceNode = node	// Do the actual traversal if we got a node	if node != nil {		var err error		// Do a simple traversal without getting any node data first		nodes, edges, err = rt.rtp.gm.TraverseMulti(rt.rtp.part, rt.sourceNode.Key(),			rt.sourceNode.Kind(), rt.spec, false)		if err != nil {			return err		}		// Now get the attributes which are required		for _, node := range nodes {			attrs := rt.rtp._attrsNodesFetch[rt.specIndex]			if len(attrs) > 0 {				n, err := rt.rtp.gm.FetchNodePart(rt.rtp.part, node.Key(), node.Kind(), attrs)				if err != nil {					return err				} else if n != nil {					for _, attr := range attrs {						node.SetAttr(attr, n.Attr(attr))					}				}			}		}		for _, edge := range edges {			attrs := rt.rtp._attrsEdgesFetch[rt.specIndex]			if len(attrs) > 0 {				e, err := rt.rtp.gm.FetchEdgePart(rt.rtp.part, edge.Key(), edge.Kind(), attrs)				if err != nil {					return err				} else if e != nil {					for _, attr := range attrs {						edge.SetAttr(attr, e.Attr(attr))					}				}			}		}	}	// Apply where clause	if rt.where != nil {		fNodes := make([]data.Node, 0, len(nodes))		fEdges := make([]data.Edge, 0, len(edges))		for i, node := range nodes {			edge := edges[i]			res, err := rt.where.Runtime.(CondRuntime).CondEval(node, edge)			if err != nil {				return err			}			if res.(bool) {				fNodes = append(fNodes, node)				fEdges = append(fEdges, edge)			}		}		nodes = fNodes		edges = fEdges	}	rt.nodes = nodes	rt.edges = edges	rt.curptr = 0	// Check if there are no nodes to display and return an error if	// empty traversals are not allowed	if len(rt.nodes) == 0 && !rt.rtp.allowNilTraversal {		return ErrEmptyTraversal	}	// Evaluate the new source	_, err := rt.Eval()	return err}/*Eval evaluate this runtime component.*/func (rt *traversalRuntime) Eval() (interface{}, error) {	// Check if a child can handle the call	for _, child := range rt.node.Children[1:] {		if child.Name == parser.NodeTRAVERSE {			childRuntime := child.Runtime.(*traversalRuntime)			if childRuntime.hasMoreNodes() {				return childRuntime.Eval()			}		}	}	// Get the next node and fill the row entry in the provider	var rowNode data.Node	var rowEdge data.Edge	if rt.curptr < len(rt.nodes) {		// Get a new node from our node list if possible		rowNode = rt.nodes[rt.curptr]		rowEdge = rt.edges[rt.curptr]		rt.curptr++	}	if len(rt.rtp.rowNode) == rt.specIndex {		rt.rtp.rowNode = append(rt.rtp.rowNode, rowNode)		rt.rtp.rowEdge = append(rt.rtp.rowEdge, rowEdge)	} else {		rt.rtp.rowNode[rt.specIndex] = rowNode		rt.rtp.rowEdge[rt.specIndex] = rowEdge	}	// Give the new source to the children and let them evaluate	for _, child := range rt.node.Children[1:] {		if child.Name == parser.NodeTRAVERSE {			childRuntime := child.Runtime.(*traversalRuntime)			if err := childRuntime.newSource(rt.rtp.rowNode[rt.specIndex]); err != nil {				return nil, err			}		}	}	return nil, nil}
 |