chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kashav/fsql/query"
|
||||
"github.com/kashav/fsql/tokenizer"
|
||||
)
|
||||
|
||||
var allAttributes = []string{"mode", "size", "time", "hash", "name"}
|
||||
|
||||
func isValidAttribute(attribute string) error {
|
||||
for _, valid := range allAttributes {
|
||||
if attribute == valid {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return &ErrUnknownToken{attribute}
|
||||
}
|
||||
|
||||
// parseAttrs parses the list of attributes passed to the SELECT clause.
|
||||
func (p *parser) parseAttrs(attributes *[]string, modifiers *map[string][]query.Modifier) error {
|
||||
for {
|
||||
ident := p.expect(tokenizer.Identifier)
|
||||
if ident == nil {
|
||||
return p.currentError()
|
||||
}
|
||||
|
||||
if ident.Raw == "*" || ident.Raw == "all" {
|
||||
*attributes = allAttributes
|
||||
} else {
|
||||
p.current = ident
|
||||
|
||||
attrModifiers := make([]query.Modifier, 0)
|
||||
attribute, err := p.parseAttr(&attrModifiers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*attributes = append(*attributes, attribute.Raw)
|
||||
(*modifiers)[attribute.Raw] = attrModifiers
|
||||
}
|
||||
|
||||
if p.expect(tokenizer.Comma) == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseAttr recursively parses an attribute's modifiers and returns the
|
||||
// associated attribute.
|
||||
func (p *parser) parseAttr(modifiers *[]query.Modifier) (*tokenizer.Token, error) {
|
||||
ident := p.expect(tokenizer.Identifier)
|
||||
if ident == nil {
|
||||
return nil, p.currentError()
|
||||
}
|
||||
|
||||
// ident is a modifier name (e.g. `FORMAT`) iff the next token is an open
|
||||
// paren, otherwise an attribute (e.g. `name`).
|
||||
if token := p.expect(tokenizer.OpenParen); token == nil {
|
||||
if err := isValidAttribute(ident.Raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ident, nil
|
||||
}
|
||||
|
||||
// In the case of chained modifiers, we want to recurse and parse each
|
||||
// inner modifier first. parseAttribute returns the associated attribute that
|
||||
// we're looking for.
|
||||
attribute, err := p.parseAttr(modifiers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if attribute == nil {
|
||||
return nil, p.currentError()
|
||||
}
|
||||
if err := isValidAttribute(attribute.Raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modifier := query.Modifier{
|
||||
Name: strings.ToUpper(ident.Raw),
|
||||
Arguments: make([]string, 0),
|
||||
}
|
||||
|
||||
// Parse the modifier arguments.
|
||||
for {
|
||||
if token := p.expect(tokenizer.Identifier); token != nil {
|
||||
modifier.Arguments = append(modifier.Arguments, token.Raw)
|
||||
continue
|
||||
}
|
||||
|
||||
if token := p.expect(tokenizer.Comma); token != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if token := p.expect(tokenizer.CloseParen); token != nil {
|
||||
*modifiers = append(*modifiers, modifier)
|
||||
return attribute, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user