func (s *Statement) OutputBindings() []string {
	var res []string
	for _, p := range s.projection {
		if p.Alias != "" {
			res = append(res, p.Alias)
			continue
		}
		if p.Binding != "" {
			res = append(res, p.Binding)
		}
	}
	set := make(map[string]bool)
	set[""] = true
	for _, c := range s.constructClauses {
		if _, ok := set[c.SBinding]; !ok {
			res = append(res, c.SBinding)
			set[c.SBinding] = true
		}
		for _, p := range c.predicateObjectPairs {
			if _, ok := set[p.PBinding]; !ok {
				res = append(res, p.PBinding)
				set[p.PBinding] = true
			}
			if _, ok := set[p.PAnchorBinding]; !ok {
				res = append(res, p.PAnchorBinding)
				set[p.PAnchorBinding] = true
			}
			if _, ok := set[p.OBinding]; !ok {
				res = append(res, p.OBinding)
				set[p.OBinding] = true
			}
			if _, ok := set[p.OAnchorBinding]; !ok {
				res = append(res, p.OAnchorBinding)
				set[p.OAnchorBinding] = true
			}
		}
	}
	return res
}
OutputBindings returns the list of binding that a query will return.
package semantic
import (
	"bytes"
	"context"
	"fmt"
	"reflect"
	"sort"
	"time"
	"github.com/google/badwolf/bql/lexer"
	"github.com/google/badwolf/bql/planner/filter"
	"github.com/google/badwolf/bql/table"
	"github.com/google/badwolf/storage"
	"github.com/google/badwolf/triple"
	"github.com/google/badwolf/triple/node"
	"github.com/google/badwolf/triple/predicate"
)
type StatementType int8
const (
	Query StatementType = iota
	Insert
	Delete
	Create
	Drop
	Construct
	Deconstruct
	Show
)
func (t StatementType) String() string {
	switch t {
	case Query:
		return "QUERY"
	case Insert:
		return "INSERT"
	case Delete:
		return "DELETE"
	case Create:
		return "CREATE"
	case Drop:
		return "DROP"
	case Construct:
		return "CONSTRUCT"
	case Deconstruct:
		return "DECONSTRUCT"
	case Show:
		return "SHOW"
	default:
		return "UNKNOWN"
	}
}
type Statement struct {
	sType                     StatementType
	graphNames                []string
	graphs                    []storage.Graph
	inputGraphNames           []string
	inputGraphs               []storage.Graph
	outputGraphNames          []string
	outputGraphs              []storage.Graph
	data                      []*triple.Triple
	pattern                   []*GraphClause
	workingClause             *GraphClause
	constructClauses          []*ConstructClause
	workingConstructClause    *ConstructClause
	projection                []*Projection
	workingProjection         *Projection
	groupBy                   []string
	orderBy                   table.SortConfig
	havingExpression          []ConsumedElement
	havingExpressionEvaluator Evaluator
	limitSet                  bool
	limit                     int64
	lookupOptions             storage.LookupOptions
	filters                   []*FilterClause
	workingFilter             *FilterClause
}
type GraphClause struct {
	Optional bool  
	S          *node.Node
	SBinding   string
	SAlias     string
	STypeAlias string
	SIDAlias   string
	P                *predicate.Predicate
	PID              string
	PBinding         string
	PAlias           string
	PIDAlias         string
	PAnchorBinding   string
	PAnchorAlias     string
	PLowerBound      *time.Time
	PUpperBound      *time.Time
	PLowerBoundAlias string
	PUpperBoundAlias string
	PTemporal        bool
	O                *triple.Object
	OBinding         string
	OAlias           string
	OID              string
	OTypeAlias       string
	OIDAlias         string
	OAnchorBinding   string
	OAnchorAlias     string
	OLowerBound      *time.Time
	OUpperBound      *time.Time
	OLowerBoundAlias string
	OUpperBoundAlias string
	OTemporal        bool
}
type FilterClause struct {
	Operation filter.Operation
	Binding   string
	Value     string
}
type ConstructClause struct {
	S        *node.Node
	SBinding string
	predicateObjectPairs       []*ConstructPredicateObjectPair
	workingPredicateObjectPair *ConstructPredicateObjectPair
}
type ConstructPredicateObjectPair struct {
	P              *predicate.Predicate
	PBinding       string
	PID            string
	PAnchorBinding string
	PTemporal      bool
	O              *triple.Object
	OBinding       string
	OID            string
	OAnchorBinding string
	OTemporal      bool
}
func (c *GraphClause) HasAlias() bool {
	return c.SAlias != "" || c.STypeAlias != "" || c.SIDAlias != "" ||
		c.PAlias != "" || c.PAnchorAlias != "" || c.PIDAlias != "" || c.PLowerBoundAlias != "" ||
		c.PUpperBoundAlias != "" || c.OAlias != "" || c.OAnchorAlias != "" || c.OIDAlias != "" ||
		c.OTypeAlias != "" || c.OLowerBoundAlias != "" || c.OUpperBoundAlias != ""
}
func (c *GraphClause) String() string {
	b := bytes.NewBufferString("{ ")
	b.WriteString("opt=")
	b.WriteString(fmt.Sprint(c.Optional))
	b.WriteString(" ")
	if c.S != nil {
		b.WriteString(c.S.String())
	} else {
		b.WriteString(c.SBinding)
	}
	if c.SAlias != "" {
		b.WriteString(" AS ")
		b.WriteString(c.SAlias)
	}
	if c.STypeAlias != "" {
		b.WriteString(" TYPE ")
		b.WriteString(c.STypeAlias)
	}
	if c.SIDAlias != "" {
		b.WriteString(" ID ")
		b.WriteString(c.SIDAlias)
	}
	pred := false
	if c.P != nil {
		b.WriteString(" ")
		b.WriteString(c.P.String())
		pred = true
	}
	if c.PBinding != "" {
		b.WriteString(" ")
		b.WriteString(c.PBinding)
	}
	if c.PID != "" {
		b.WriteString(" \"")
		b.WriteString(c.PID)
		b.WriteString("\"")
	}
	if !pred {
		if !c.PTemporal {
			b.WriteString("@[]")
		} else {
			b.WriteString("@[")
			if c.PAnchorBinding != "" {
				b.WriteString(c.PAnchorBinding)
				if c.PAnchorAlias != "" {
					b.WriteString(" at ")
					b.WriteString(c.PAnchorAlias)
				}
			} else {
				if c.PLowerBound != nil {
					b.WriteString(c.PLowerBound.Format(time.RFC3339Nano))
				} else {
					if c.PLowerBoundAlias != "" {
						b.WriteString(c.PLowerBoundAlias)
					}
				}
				b.WriteString(",")
				if c.PUpperBound != nil {
					b.WriteString(c.PUpperBound.Format(time.RFC3339Nano))
				} else {
					if c.PUpperBoundAlias != "" {
						b.WriteString(c.PUpperBoundAlias)
					}
				}
			}
			b.WriteString("]")
		}
	}
	if c.PAlias != "" {
		b.WriteString(" AS ")
		b.WriteString(c.PAlias)
	}
	if c.PIDAlias != "" {
		b.WriteString(" ID ")
		b.WriteString(c.PIDAlias)
	}
	if c.PAnchorAlias != "" {
		b.WriteString(" AT ")
		b.WriteString(c.PAnchorAlias)
	}
	object := false
	if c.O != nil {
		b.WriteString(" ")
		b.WriteString(c.O.String())
		object = true
	}
	if !object {
		if c.OBinding != "" {
			b.WriteString(" ")
			b.WriteString(c.OBinding)
		}
		if c.OID != "" {
			b.WriteString(" \"")
			b.WriteString(c.OID)
			b.WriteString("\"")
		}
		if !c.OTemporal {
			b.WriteString("[]")
		} else {
			b.WriteString("[")
			if c.OAnchorBinding != "" {
				b.WriteString(c.OAnchorBinding)
				if c.OAnchorAlias != "" {
					b.WriteString(" at ")
					b.WriteString(c.OAnchorAlias)
				}
			} else {
				if c.OLowerBound != nil {
					b.WriteString(c.OLowerBound.Format(time.RFC3339Nano))
				} else {
					if c.OLowerBoundAlias != "" {
						b.WriteString(c.OLowerBoundAlias)
					}
				}
				b.WriteString(",")
				if c.OUpperBound != nil {
					b.WriteString(c.OUpperBound.Format(time.RFC3339Nano))
				} else {
					if c.OUpperBoundAlias != "" {
						b.WriteString(c.OUpperBoundAlias)
					}
				}
			}
			b.WriteString("]")
		}
	}
	if c.OAlias != "" {
		b.WriteString(" AS ")
		b.WriteString(c.OAlias)
	}
	if c.OTypeAlias != "" {
		b.WriteString(" TYPE ")
		b.WriteString(c.OTypeAlias)
	}
	if c.OIDAlias != "" {
		b.WriteString(" ID ")
		b.WriteString(c.OIDAlias)
	}
	if c.OAnchorAlias != "" {
		b.WriteString(" AT ")
		b.WriteString(c.OAnchorAlias)
	}
	if c.OAlias != "" {
		b.WriteString(" AS ")
		b.WriteString(c.OAlias)
	}
	if c.OIDAlias != "" {
		b.WriteString(" ID ")
		b.WriteString(c.OIDAlias)
	}
	b.WriteString(" }")
	return b.String()
}
func (c *GraphClause) Specificity() int {
	s := 0
	if c.S != nil {
		s++
	}
	if c.P != nil {
		s++
	}
	if c.O != nil {
		s++
	}
	return s
}
func (c *GraphClause) BindingsMap() map[string]int {
	bm := make(map[string]int)
	addToBindings(bm, c.SBinding)
	addToBindings(bm, c.SAlias)
	addToBindings(bm, c.STypeAlias)
	addToBindings(bm, c.SIDAlias)
	addToBindings(bm, c.PAlias)
	addToBindings(bm, c.PAnchorBinding)
	addToBindings(bm, c.PBinding)
	addToBindings(bm, c.PLowerBoundAlias)
	addToBindings(bm, c.PUpperBoundAlias)
	addToBindings(bm, c.PIDAlias)
	addToBindings(bm, c.PAnchorAlias)
	addToBindings(bm, c.OBinding)
	addToBindings(bm, c.OAlias)
	addToBindings(bm, c.OTypeAlias)
	addToBindings(bm, c.OIDAlias)
	addToBindings(bm, c.OAnchorAlias)
	addToBindings(bm, c.OAnchorBinding)
	addToBindings(bm, c.OLowerBoundAlias)
	addToBindings(bm, c.OUpperBoundAlias)
	return bm
}
func (c *GraphClause) Bindings() []string {
	var bs []string
	for k := range c.BindingsMap() {
		bs = append(bs, k)
	}
	return bs
}
func (c *GraphClause) IsEmpty() bool {
	return reflect.DeepEqual(c, &GraphClause{})
}
func (f *FilterClause) IsEmpty() bool {
	return reflect.DeepEqual(f, &FilterClause{})
}
func (f *FilterClause) String() string {
	return fmt.Sprintf("%+v", *f)
}
func (c *ConstructClause) String() string {
	b := bytes.NewBufferString("{ ")
	if c.S != nil {
		b.WriteString(c.S.String())
	} else {
		b.WriteString(c.SBinding)
	}
	for _, pop := range c.PredicateObjectPairs() {
		b.WriteString(fmt.Sprintf("%v;", pop))
	}
	b.Truncate(b.Len() - 1)
	b.WriteString(" }")
	return b.String()
}
func (c *ConstructClause) IsEmpty() bool {
	return reflect.DeepEqual(c, &ConstructClause{})
}
func (c *ConstructPredicateObjectPair) String() string {
	b := bytes.NewBufferString("")
	pred := false
	if c.P != nil {
		b.WriteString(" ")
		b.WriteString(c.P.String())
		pred = true
	}
	if c.PBinding != "" {
		b.WriteString(" ")
		b.WriteString(c.PBinding)
	}
	if c.PID != "" {
		b.WriteString(" \"")
		b.WriteString(c.PID)
		b.WriteString("\"")
	}
	if !pred {
		if !c.PTemporal {
			b.WriteString("@[]")
		} else {
			b.WriteString("@[")
			if c.PAnchorBinding != "" {
				b.WriteString(c.PAnchorBinding)
			}
		}
		b.WriteString("]")
	}
	if c.O != nil {
		b.WriteString(" ")
		b.WriteString(c.O.String())
	} else {
		if c.OBinding != "" {
			b.WriteString(" ")
			b.WriteString(c.OBinding)
		}
		if c.OID != "" {
			b.WriteString(" \"")
			b.WriteString(c.OID)
			b.WriteString("\"")
		}
		if !c.OTemporal {
			b.WriteString("[]")
		} else {
			b.WriteString("[")
			if c.OAnchorBinding != "" {
				b.WriteString(c.OAnchorBinding)
			}
			b.WriteString("]")
		}
	}
	return b.String()
}
func (c *ConstructPredicateObjectPair) IsEmpty() bool {
	return reflect.DeepEqual(c, &ConstructPredicateObjectPair{})
}
func (s *Statement) BindType(st StatementType) {
	s.sType = st
}
func (s *Statement) Type() StatementType {
	return s.sType
}
func (s *Statement) AddGraph(g string) {
	s.graphNames = append(s.graphNames, g)
}
func (s *Statement) Graphs() []storage.Graph {
	return s.graphs
}
func (s *Statement) InputGraphNames() []string {
	return s.inputGraphNames
}
func (s *Statement) AddInputGraph(g string) {
	s.inputGraphNames = append(s.inputGraphNames, g)
}
func (s *Statement) InputGraphs() []storage.Graph {
	return s.inputGraphs
}
func (s *Statement) OutputGraphNames() []string {
	return s.outputGraphNames
}
func (s *Statement) AddOutputGraph(g string) {
	s.outputGraphNames = append(s.outputGraphNames, g)
}
func (s *Statement) OutputGraphs() []storage.Graph {
	return s.outputGraphs
}
func (s *Statement) Init(ctx context.Context, st storage.Store) error {
	for _, gn := range s.graphNames {
		g, err := st.Graph(ctx, gn)
		if err != nil {
			return err
		}
		s.graphs = append(s.graphs, g)
	}
	for _, ign := range s.inputGraphNames {
		ig, err := st.Graph(ctx, ign)
		if err != nil {
			return err
		}
		s.inputGraphs = append(s.inputGraphs, ig)
	}
	for _, ogn := range s.outputGraphNames {
		og, err := st.Graph(ctx, ogn)
		if err != nil {
			return err
		}
		s.outputGraphs = append(s.outputGraphs, og)
	}
	return nil
}
func (s *Statement) GraphNames() []string {
	return s.graphNames
}
func (s *Statement) AddData(d *triple.Triple) {
	s.data = append(s.data, d)
}
func (s *Statement) Data() []*triple.Triple {
	return s.data
}
func (s *Statement) GraphPatternClauses() []*GraphClause {
	return s.pattern
}
func (s *Statement) FilterClauses() []*FilterClause {
	return s.filters
}
func (s *Statement) ResetWorkingGraphClause() {
	s.workingClause = &GraphClause{}
}
func (s *Statement) ResetWorkingFilterClause() {
	s.workingFilter = &FilterClause{}
}
func (s *Statement) WorkingClause() *GraphClause {
	return s.workingClause
}
func (s *Statement) WorkingFilter() *FilterClause {
	return s.workingFilter
}
func (s *Statement) AddWorkingGraphClause() {
	if s.workingClause != nil && !s.workingClause.IsEmpty() {
		s.pattern = append(s.pattern, s.workingClause)
	}
	s.ResetWorkingGraphClause()
}
func (s *Statement) AddWorkingFilterClause() {
	s.filters = append(s.filters, s.workingFilter)
	s.ResetWorkingFilterClause()
}
func (s *Statement) Projection() []*Projection {
	return s.projection
}
func (s *Statement) GroupBy() []string {
	return s.groupBy
}
func (s *Statement) OrderBy() table.SortConfig {
	return s.orderBy
}
func (s *Statement) HavingExpression() []ConsumedElement {
	return s.havingExpression
}
func (s *Statement) HasLimit() bool {
	return s.limitSet
}
func addToBindings(bs map[string]int, b string) {
	if b != "" {
		bs[b]++
	}
}
func (s *Statement) BindingsMap() map[string]int {
	bm := make(map[string]int)
	for _, cls := range s.pattern {
		if cls != nil {
			addToBindings(bm, cls.SBinding)
			addToBindings(bm, cls.SAlias)
			addToBindings(bm, cls.STypeAlias)
			addToBindings(bm, cls.SIDAlias)
			addToBindings(bm, cls.PAlias)
			addToBindings(bm, cls.PAnchorBinding)
			addToBindings(bm, cls.PBinding)
			addToBindings(bm, cls.PLowerBoundAlias)
			addToBindings(bm, cls.PUpperBoundAlias)
			addToBindings(bm, cls.PIDAlias)
			addToBindings(bm, cls.PAnchorAlias)
			addToBindings(bm, cls.OBinding)
			addToBindings(bm, cls.OAlias)
			addToBindings(bm, cls.OTypeAlias)
			addToBindings(bm, cls.OIDAlias)
			addToBindings(bm, cls.OAnchorAlias)
			addToBindings(bm, cls.OAnchorBinding)
			addToBindings(bm, cls.OLowerBoundAlias)
			addToBindings(bm, cls.OUpperBoundAlias)
		}
	}
	return bm
}
func (s *Statement) Bindings() []string {
	var bs []string
	for k := range s.BindingsMap() {
		bs = append(bs, k)
	}
	return bs
}
type bySpecificity []*GraphClause
func (s bySpecificity) Len() int {
	return len(s)
}
func (s bySpecificity) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s bySpecificity) Less(i, j int) bool {
	return s[i].Specificity() >= s[j].Specificity()
}
func (s *Statement) SortedGraphPatternClauses() []*GraphClause {
	var ptrns []*GraphClause
	for _, cls := range s.pattern {
		if cls != nil && !cls.IsEmpty() {
			ptrns = append(ptrns, cls)
		}
	}
	sort.Sort(bySpecificity(ptrns))
	return ptrns
}
type Projection struct {
	Binding  string
	Alias    string
	OP       lexer.TokenType  
	Modifier lexer.TokenType  
}
func (p *Projection) String() string {
	b := bytes.NewBufferString(p.Binding)
	b.WriteString(" as ")
	b.WriteString(p.Binding)
	if p.OP != lexer.ItemError {
		b.WriteString(" via ")
		b.WriteString(p.OP.String())
		if p.Modifier != lexer.ItemError {
			b.WriteString(" ")
			b.WriteString(p.Modifier.String())
		}
	}
	return b.String()
}
func (p *Projection) IsEmpty() bool {
	return p.Binding == "" && p.Alias == "" && p.OP == lexer.ItemError && p.Modifier == lexer.ItemError
}
func (s *Statement) ResetProjection() {
	s.workingProjection = &Projection{}
}
func (s *Statement) WorkingProjection() *Projection {
	if s.workingProjection == nil {
		s.ResetProjection()
	}
	return s.workingProjection
}
func (s *Statement) AddWorkingProjection() {
	if s.workingProjection != nil && !s.workingProjection.IsEmpty() {
		s.projection = append(s.projection, s.workingProjection)
	}
	s.ResetProjection()
}
func (s *Statement) Projections() []*Projection {
	return s.projection
}
func (s *Statement) InputBindings() []string {
	var res []string
	for _, p := range s.projection {
		if p.Binding != "" {
			res = append(res, p.Binding)
		}
	}
	for _, c := range s.constructClauses {
		if c.SBinding != "" {
			res = append(res, c.SBinding)
		}
		for _, p := range c.predicateObjectPairs {
			if p.PBinding != "" {
				res = append(res, p.PBinding)
			}
			if p.PAnchorBinding != "" {
				res = append(res, p.PAnchorBinding)
			}
			if p.OBinding != "" {
				res = append(res, p.OBinding)
			}
			if p.OAnchorBinding != "" {
				res = append(res, p.OAnchorBinding)
			}
		}
	}
	return res
}