github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/pubsub/query/syntax/parser.go (about)

     1  package syntax
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"math"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  // Parse parses the specified query string. It is shorthand for constructing a
    13  // parser for s and calling its Parse method.
    14  func Parse(s string) (Query, error) {
    15  	return NewParser(strings.NewReader(s)).Parse()
    16  }
    17  
    18  // Query is the root of the parse tree for a query.  A query is the conjunction
    19  // of one or more conditions.
    20  type Query []Condition
    21  
    22  func (q Query) String() string {
    23  	ss := make([]string, len(q))
    24  	for i, cond := range q {
    25  		ss[i] = cond.String()
    26  	}
    27  	return strings.Join(ss, " AND ")
    28  }
    29  
    30  // A Condition is a single conditional expression, consisting of a tag, a
    31  // comparison operator, and an optional argument. The type of the argument
    32  // depends on the operator.
    33  type Condition struct {
    34  	Tag string
    35  	Op  Token
    36  	Arg *Arg
    37  
    38  	opText string
    39  }
    40  
    41  func (c Condition) String() string {
    42  	s := c.Tag + " " + c.opText
    43  	if c.Arg != nil {
    44  		return s + " " + c.Arg.String()
    45  	}
    46  	return s
    47  }
    48  
    49  // An Arg is the argument of a comparison operator.
    50  type Arg struct {
    51  	Type Token
    52  	text string
    53  }
    54  
    55  func (a *Arg) String() string {
    56  	if a == nil {
    57  		return ""
    58  	}
    59  	switch a.Type {
    60  	case TString:
    61  		return "'" + a.text + "'"
    62  	case TTime:
    63  		return "TIME " + a.text
    64  	case TDate:
    65  		return "DATE " + a.text
    66  	default:
    67  		return a.text
    68  	}
    69  }
    70  
    71  // Number returns the value of the argument text as a number, or a NaN if the
    72  // text does not encode a valid number value.
    73  func (a *Arg) Number() float64 {
    74  	if a == nil {
    75  		return -1
    76  	}
    77  	v, err := strconv.ParseFloat(a.text, 64)
    78  	if err == nil && v >= 0 {
    79  		return v
    80  	}
    81  	return math.NaN()
    82  }
    83  
    84  // Time returns the value of the argument text as a time, or the zero value if
    85  // the text does not encode a timestamp or datestamp.
    86  func (a *Arg) Time() time.Time {
    87  	var ts time.Time
    88  	if a == nil {
    89  		return ts
    90  	}
    91  	var err error
    92  	switch a.Type {
    93  	case TDate:
    94  		ts, err = ParseDate(a.text)
    95  	case TTime:
    96  		ts, err = ParseTime(a.text)
    97  	}
    98  	if err == nil {
    99  		return ts
   100  	}
   101  	return time.Time{}
   102  }
   103  
   104  // Value returns the value of the argument text as a string, or "".
   105  func (a *Arg) Value() string {
   106  	if a == nil {
   107  		return ""
   108  	}
   109  	return a.text
   110  }
   111  
   112  // Parser is a query expression parser. The grammar for query expressions is
   113  // defined in the syntax package documentation.
   114  type Parser struct {
   115  	scanner *Scanner
   116  }
   117  
   118  // NewParser constructs a new parser that reads the input from r.
   119  func NewParser(r io.Reader) *Parser {
   120  	return &Parser{scanner: NewScanner(r)}
   121  }
   122  
   123  // Parse parses the complete input and returns the resulting query.
   124  func (p *Parser) Parse() (Query, error) {
   125  	cond, err := p.parseCond()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	conds := []Condition{cond}
   130  	for p.scanner.Next() != io.EOF {
   131  		if tok := p.scanner.Token(); tok != TAnd {
   132  			return nil, fmt.Errorf("offset %d: got %v, want %v", p.scanner.Pos(), tok, TAnd)
   133  		}
   134  		cond, err := p.parseCond()
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		conds = append(conds, cond)
   139  	}
   140  	return conds, nil
   141  }
   142  
   143  // parseCond parses a conditional expression: tag OP value.
   144  func (p *Parser) parseCond() (Condition, error) {
   145  	var cond Condition
   146  	if err := p.require(TTag); err != nil {
   147  		return cond, err
   148  	}
   149  	cond.Tag = p.scanner.Text()
   150  	if err := p.require(TLeq, TGeq, TLt, TGt, TEq, TContains, TExists); err != nil {
   151  		return cond, err
   152  	}
   153  	cond.Op = p.scanner.Token()
   154  	cond.opText = p.scanner.Text()
   155  
   156  	var err error
   157  	switch cond.Op {
   158  	case TLeq, TGeq, TLt, TGt:
   159  		err = p.require(TNumber, TTime, TDate)
   160  	case TEq:
   161  		err = p.require(TNumber, TTime, TDate, TString)
   162  	case TContains:
   163  		err = p.require(TString)
   164  	case TExists:
   165  		// no argument
   166  		return cond, nil
   167  	default:
   168  		return cond, fmt.Errorf("offset %d: unexpected operator %v", p.scanner.Pos(), cond.Op)
   169  	}
   170  	if err != nil {
   171  		return cond, err
   172  	}
   173  	cond.Arg = &Arg{Type: p.scanner.Token(), text: p.scanner.Text()}
   174  	return cond, nil
   175  }
   176  
   177  // require advances the scanner and requires that the resulting token is one of
   178  // the specified token types.
   179  func (p *Parser) require(tokens ...Token) error {
   180  	if err := p.scanner.Next(); err != nil {
   181  		return fmt.Errorf("offset %d: %w", p.scanner.Pos(), err)
   182  	}
   183  	got := p.scanner.Token()
   184  	for _, tok := range tokens {
   185  		if tok == got {
   186  			return nil
   187  		}
   188  	}
   189  	return fmt.Errorf("offset %d: got %v, wanted %s", p.scanner.Pos(), got, tokLabel(tokens))
   190  }
   191  
   192  // tokLabel makes a human-readable summary string for the given token types.
   193  func tokLabel(tokens []Token) string {
   194  	if len(tokens) == 1 {
   195  		return tokens[0].String()
   196  	}
   197  	last := len(tokens) - 1
   198  	ss := make([]string, len(tokens)-1)
   199  	for i, tok := range tokens[:last] {
   200  		ss[i] = tok.String()
   201  	}
   202  	return strings.Join(ss, ", ") + " or " + tokens[last].String()
   203  }
   204  
   205  // ParseDate parses s as a date string in the format used by DATE values.
   206  func ParseDate(s string) (time.Time, error) {
   207  	return time.Parse("2006-01-02", s)
   208  }
   209  
   210  // ParseTime parses s as a timestamp in the format used by TIME values.
   211  func ParseTime(s string) (time.Time, error) {
   212  	return time.Parse(time.RFC3339, s)
   213  }