Adding upstream version 0.5.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
f9051e9424
commit
16e40566d2
8 changed files with 1303 additions and 0 deletions
130
parser.go
Normal file
130
parser.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package fexpr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var ErrEmpty = errors.New("empty filter expression")
|
||||
var ErrIncomplete = errors.New("invalid or incomplete filter expression")
|
||||
var ErrInvalidComment = errors.New("invalid comment")
|
||||
|
||||
// Expr represents an individual tokenized expression consisting
|
||||
// of left operand, operator and a right operand.
|
||||
type Expr struct {
|
||||
Left Token
|
||||
Op SignOp
|
||||
Right Token
|
||||
}
|
||||
|
||||
// IsZero checks if the current Expr has zero-valued props.
|
||||
func (e Expr) IsZero() bool {
|
||||
return e.Op == "" && e.Left.Literal == "" && e.Left.Type == "" && e.Right.Literal == "" && e.Right.Type == ""
|
||||
}
|
||||
|
||||
// ExprGroup represents a wrapped expression and its join type.
|
||||
//
|
||||
// The group's Item could be either an `Expr` instance or `[]ExprGroup` slice (for nested expressions).
|
||||
type ExprGroup struct {
|
||||
Item interface{}
|
||||
Join JoinOp
|
||||
}
|
||||
|
||||
// parser's state machine steps
|
||||
const (
|
||||
stepBeforeSign = iota
|
||||
stepSign
|
||||
stepAfterSign
|
||||
StepJoin
|
||||
)
|
||||
|
||||
// Parse parses the provided text and returns its processed AST
|
||||
// in the form of `ExprGroup` slice(s).
|
||||
//
|
||||
// Comments and whitespaces are ignored.
|
||||
func Parse(text string) ([]ExprGroup, error) {
|
||||
result := []ExprGroup{}
|
||||
scanner := NewScanner([]byte(text))
|
||||
step := stepBeforeSign
|
||||
join := JoinAnd
|
||||
|
||||
var expr Expr
|
||||
|
||||
for {
|
||||
t, err := scanner.Scan()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t.Type == TokenEOF {
|
||||
break
|
||||
}
|
||||
|
||||
if t.Type == TokenWS || t.Type == TokenComment {
|
||||
continue
|
||||
}
|
||||
|
||||
if t.Type == TokenGroup {
|
||||
groupResult, err := Parse(t.Literal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// append only if non-empty group
|
||||
if len(groupResult) > 0 {
|
||||
result = append(result, ExprGroup{Join: join, Item: groupResult})
|
||||
}
|
||||
|
||||
step = StepJoin
|
||||
continue
|
||||
}
|
||||
|
||||
switch step {
|
||||
case stepBeforeSign:
|
||||
if t.Type != TokenIdentifier && t.Type != TokenText && t.Type != TokenNumber && t.Type != TokenFunction {
|
||||
return nil, fmt.Errorf("expected left operand (identifier, function, text or number), got %q (%s)", t.Literal, t.Type)
|
||||
}
|
||||
|
||||
expr = Expr{Left: t}
|
||||
|
||||
step = stepSign
|
||||
case stepSign:
|
||||
if t.Type != TokenSign {
|
||||
return nil, fmt.Errorf("expected a sign operator, got %q (%s)", t.Literal, t.Type)
|
||||
}
|
||||
|
||||
expr.Op = SignOp(t.Literal)
|
||||
step = stepAfterSign
|
||||
case stepAfterSign:
|
||||
if t.Type != TokenIdentifier && t.Type != TokenText && t.Type != TokenNumber && t.Type != TokenFunction {
|
||||
return nil, fmt.Errorf("expected right operand (identifier, function text or number), got %q (%s)", t.Literal, t.Type)
|
||||
}
|
||||
|
||||
expr.Right = t
|
||||
result = append(result, ExprGroup{Join: join, Item: expr})
|
||||
|
||||
step = StepJoin
|
||||
case StepJoin:
|
||||
if t.Type != TokenJoin {
|
||||
return nil, fmt.Errorf("expected && or ||, got %q (%s)", t.Literal, t.Type)
|
||||
}
|
||||
|
||||
join = JoinAnd
|
||||
if t.Literal == "||" {
|
||||
join = JoinOr
|
||||
}
|
||||
|
||||
step = stepBeforeSign
|
||||
}
|
||||
}
|
||||
|
||||
if step != StepJoin {
|
||||
if len(result) == 0 && expr.IsZero() {
|
||||
return nil, ErrEmpty
|
||||
}
|
||||
|
||||
return nil, ErrIncomplete
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue