github.com/cayleygraph/cayley@v0.7.7/query/sexp/session.go (about)

     1  // Copyright 2014 The Cayley Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package sexp
    16  
    17  // Defines a running session of the sexp query language.
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"sort"
    24  
    25  	"github.com/cayleygraph/cayley/graph"
    26  	"github.com/cayleygraph/cayley/graph/iterator"
    27  	"github.com/cayleygraph/cayley/query"
    28  )
    29  
    30  const Name = "sexp"
    31  
    32  func init() {
    33  	query.RegisterLanguage(query.Language{
    34  		Name: Name,
    35  		Session: func(qs graph.QuadStore) query.Session {
    36  			return NewSession(qs)
    37  		},
    38  		REPL: func(qs graph.QuadStore) query.REPLSession {
    39  			return NewSession(qs)
    40  		},
    41  	})
    42  }
    43  
    44  type Session struct {
    45  	qs graph.QuadStore
    46  }
    47  
    48  func NewSession(qs graph.QuadStore) *Session {
    49  	return &Session{qs: qs}
    50  }
    51  
    52  func (s *Session) Parse(input string) error {
    53  	var parenDepth int
    54  	for i, x := range input {
    55  		if x == '(' {
    56  			parenDepth++
    57  		}
    58  		if x == ')' {
    59  			parenDepth--
    60  			if parenDepth < 0 {
    61  				min := 0
    62  				if (i - 10) > min {
    63  					min = i - 10
    64  				}
    65  				return fmt.Errorf("too many close parentheses at char %d: %s", i, input[min:i])
    66  			}
    67  		}
    68  	}
    69  	if parenDepth > 0 {
    70  		return query.ErrParseMore
    71  	}
    72  	if len(ParseString(input)) > 0 {
    73  		return nil
    74  	}
    75  	return errors.New("invalid syntax")
    76  }
    77  
    78  func (s *Session) Execute(ctx context.Context, input string, opt query.Options) (query.Iterator, error) {
    79  	switch opt.Collation {
    80  	case query.Raw, query.REPL:
    81  	default:
    82  		return nil, &query.ErrUnsupportedCollation{Collation: opt.Collation}
    83  	}
    84  	it := BuildIteratorTreeForQuery(s.qs, input)
    85  	if err := it.Err(); err != nil {
    86  		return nil, err
    87  	}
    88  	if opt.Limit > 0 {
    89  		it = iterator.NewLimit(it, int64(opt.Limit))
    90  	}
    91  	return &results{
    92  		s:   s,
    93  		col: opt.Collation,
    94  		it:  it,
    95  	}, nil
    96  }
    97  
    98  type results struct {
    99  	s        *Session
   100  	col      query.Collation
   101  	it       graph.Iterator
   102  	nextPath bool
   103  }
   104  
   105  func (it *results) Next(ctx context.Context) bool {
   106  	if it.nextPath && it.it.NextPath(ctx) {
   107  		return true
   108  	}
   109  	it.nextPath = false
   110  	if it.it.Next(ctx) {
   111  		it.nextPath = true
   112  		return true
   113  	}
   114  	return false
   115  }
   116  
   117  func (it *results) Result() interface{} {
   118  	m := make(map[string]graph.Ref)
   119  	it.it.TagResults(m)
   120  	if it.col == query.Raw {
   121  		return m
   122  	}
   123  	out := "****\n"
   124  	tagKeys := make([]string, len(m))
   125  	i := 0
   126  	for k := range m {
   127  		tagKeys[i] = k
   128  		i++
   129  	}
   130  	sort.Strings(tagKeys)
   131  	for _, k := range tagKeys {
   132  		if k == "$_" {
   133  			continue
   134  		}
   135  		out += fmt.Sprintf("%s : %s\n", k, it.s.qs.NameOf(m[k]))
   136  	}
   137  	return out
   138  }
   139  
   140  func (it *results) Err() error {
   141  	return it.it.Err()
   142  }
   143  
   144  func (it *results) Close() error {
   145  	return it.it.Close()
   146  }