Skip to content

To support single quote in Filter #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions attrexp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,18 @@ package filter
import (
"encoding/json"
"fmt"
"strconv"
"strings"

"github.com/di-wu/parser"
"github.com/di-wu/parser/ast"

"github.com/scim2/filter-parser/v2/internal/grammar"
"github.com/scim2/filter-parser/v2/internal/types"
"strconv"
"strings"
typ "github.com/scim2/filter-parser/v2/internal/types"
)

// ParseAttrExp parses the given raw data as an AttributeExpression.
func ParseAttrExp(raw []byte) (AttributeExpression, error) {
return parseAttrExp(raw, config{})
}

// ParseAttrExpNumber parses the given raw data as an AttributeExpression with json.Number.
func ParseAttrExpNumber(raw []byte) (AttributeExpression, error) {
return parseAttrExp(raw, config{useNumber: true})
}

func parseAttrExp(raw []byte, c config) (AttributeExpression, error) {
func ParseAttrExp(raw []byte, options ...configOption) (AttributeExpression, error) {
p, err := ast.New(raw)
if err != nil {
return AttributeExpression{}, err
Expand All @@ -33,7 +26,14 @@ func parseAttrExp(raw []byte, c config) (AttributeExpression, error) {
if _, err := p.Expect(parser.EOD); err != nil {
return AttributeExpression{}, err
}
return c.parseAttrExp(node)
return getConfig(options...).parseAttrExp(node)
}

// ParseAttrExpNumber parses the given raw data as an AttributeExpression with json.Number.
//
// Deprecated - use ParseAttrExpNumber WithUseNumber option
func ParseAttrExpNumber(raw []byte) (AttributeExpression, error) {
return ParseAttrExp(raw, WithUseNumber())
}

func (p config) parseAttrExp(node *ast.Node) (AttributeExpression, error) {
Expand Down Expand Up @@ -80,11 +80,16 @@ func (p config) parseAttrExp(node *ast.Node) (AttributeExpression, error) {
return AttributeExpression{}, err
}
compareValue = value
case typ.String:
case typ.StringWithDoubleQuotes:
str := node.Value
str = strings.TrimPrefix(str, "\"")
str = strings.TrimSuffix(str, "\"")
compareValue = str
case typ.StringWithSingleQuotes:
str := node.Value
str = strings.TrimPrefix(str, "'")
str = strings.TrimSuffix(str, "'")
compareValue = str
default:
return AttributeExpression{}, invalidChildTypeError(typ.AttrExp, node.Type)
}
Expand Down
18 changes: 18 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,21 @@ type config struct {
// useNumber indicates that json.Number needs to be returned instead of int/float64 values.
useNumber bool
}

type configOption func(config) config

// WithUseNumber set use number to true
func WithUseNumber() configOption {
return func(c config) config {
c.useNumber = true
return c
}
}

func getConfig(options ...configOption) config {
c := config{}
for _, opt := range options {
c = opt(c)
}
return c
}
22 changes: 18 additions & 4 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@ package filter
import (
"github.com/di-wu/parser"
"github.com/di-wu/parser/ast"

"github.com/scim2/filter-parser/v2/internal/grammar"
"github.com/scim2/filter-parser/v2/internal/types"
typ "github.com/scim2/filter-parser/v2/internal/types"
)

// ParseFilter parses the given raw data as an Expression.
func ParseFilter(raw []byte) (Expression, error) {
return parseFilter(raw, config{})
func ParseFilter(raw []byte, options ...configOption) (Expression, error) {
p, err := ast.New(raw)
if err != nil {
return nil, err
}
node, err := grammar.Filter(p)
if err != nil {
return nil, err
}
if _, err := p.Expect(parser.EOD); err != nil {
return nil, err
}
return getConfig(options...).parseFilterOr(node)
}

// ParseFilterNumber parses the given raw data as an Expression with json.Number.
//
// Deprecated - use ParseFilter WithUseNumber option
func ParseFilterNumber(raw []byte) (Expression, error) {
return parseFilter(raw, config{useNumber: true})
return ParseFilter(raw, WithUseNumber())
}

func parseFilter(raw []byte, c config) (Expression, error) {
Expand Down
4 changes: 2 additions & 2 deletions filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ func ExampleParseFilter_parentheses() {
}

func ExampleParseFilter_valuePath() {
fmt.Println(ParseFilter([]byte("emails[type eq \"work\" and value co \"@example.com\"]")))
fmt.Println(ParseFilter([]byte("emails[type eq \"work\" and value co '@example.com']")))
// Output:
// emails[type eq "work" and value co "@example.com"] <nil>
}

func Example_walk() {
expression, _ := ParseFilter([]byte("emails[type eq \"work\" and value co \"@example.com\"] or ims[type eq \"xmpp\" and value co \"@foo.com\"]"))
expression, _ := ParseFilter([]byte("emails[type eq \"work\" and value co \"@example.com\"] or ims[type eq \"xmpp\" and value co '@foo.com']"))
var walk func(e Expression) error
walk = func(e Expression) error {
switch v := e.(type) {
Expand Down
5 changes: 3 additions & 2 deletions internal/grammar/attrexp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"github.com/di-wu/parser"
"github.com/di-wu/parser/ast"
"github.com/di-wu/parser/op"
"github.com/scim2/filter-parser/v2/internal/types"

typ "github.com/scim2/filter-parser/v2/internal/types"
)

func AttrExp(p *ast.Parser) (*ast.Node, error) {
Expand Down Expand Up @@ -69,7 +70,7 @@ func CompareOp(p *ast.Parser) (*ast.Node, error) {
}

func CompareValue(p *ast.Parser) (*ast.Node, error) {
return p.Expect(op.Or{False, Null, True, Number, String})
return p.Expect(op.Or{False, Null, True, Number, String(doubleQuote), String(singleQuote)})
}

func NameChar(p *ast.Parser) (*ast.Node, error) {
Expand Down
1 change: 1 addition & 0 deletions internal/grammar/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package grammar

import (
"fmt"

"github.com/di-wu/parser/ast"
)

Expand Down
120 changes: 88 additions & 32 deletions internal/grammar/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,60 @@ import (
"github.com/di-wu/parser"
"github.com/di-wu/parser/ast"
"github.com/di-wu/parser/op"
"github.com/scim2/filter-parser/v2/internal/types"

typ "github.com/scim2/filter-parser/v2/internal/types"
)

func Character(p *ast.Parser) (*ast.Node, error) {
return p.Expect(
op.Or{
Unescaped,
op.And{
"\\",
Escaped,
type quote int

const (
singleQuote quote = iota
doubleQuote
)

func (q quote) getValue() string {
switch q {
case singleQuote:
return "'"
case doubleQuote:
fallthrough
default:
return "\""
}
}

func (q quote) getType() int {
switch q {
case singleQuote:
return typ.StringWithSingleQuotes
case doubleQuote:
fallthrough
default:
return typ.StringWithDoubleQuotes
}
}

func Character(q quote) func(*ast.Parser) (*ast.Node, error) {
return func(p *ast.Parser) (*ast.Node, error) {
return p.Expect(
op.Or{
Unescaped(q),
op.And{
"\\",
q.getValue(),
},
op.And{
"\\",
Escaped,
},
},
},
)
)
}
}

func Escaped(p *ast.Parser) (*ast.Node, error) {
return p.Expect(
op.Or{
"\"",
"\\",
"/",
0x0062,
Expand All @@ -43,28 +78,49 @@ func Escaped(p *ast.Parser) (*ast.Node, error) {
)
}

func String(p *ast.Parser) (*ast.Node, error) {
return p.Expect(
ast.Capture{
Type: typ.String,
TypeStrings: typ.Stringer,
Value: op.And{
"\"",
op.MinZero(
Character,
),
"\"",
func String(q quote) func(*ast.Parser) (*ast.Node, error) {
return func(p *ast.Parser) (*ast.Node, error) {
return p.Expect(
ast.Capture{
Type: q.getType(),
TypeStrings: typ.Stringer,
Value: op.And{
q.getValue(),
op.MinZero(
Character(q),
),
q.getValue(),
},
},
},
)
)
}
}

func Unescaped(p *ast.Parser) (*ast.Node, error) {
return p.Expect(
op.Or{
parser.CheckRuneRange(0x0020, 0x0021),
parser.CheckRuneRange(0x0023, 0x005B),
parser.CheckRuneRange(0x005D, 0x0010FFFF),
},
)
func Unescaped(q quote) func(*ast.Parser) (*ast.Node, error) {
switch q {
case singleQuote:
return func(p *ast.Parser) (*ast.Node, error) {
// 0x0027 : '
return p.Expect(
op.Or{
parser.CheckRuneRange(0x0020, 0x0026),
parser.CheckRuneRange(0x0028, 0x0010FFFF),
},
)
}
case doubleQuote:
fallthrough
default:
return func(p *ast.Parser) (*ast.Node, error) {
// 0x0023 : "
// 0x005C : \
return p.Expect(
op.Or{
parser.CheckRuneRange(0x0020, 0x0021),
parser.CheckRuneRange(0x0023, 0x005B),
parser.CheckRuneRange(0x005D, 0x0010FFFF),
},
)
}
}
}
19 changes: 17 additions & 2 deletions internal/grammar/string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,34 @@ package grammar

import (
"fmt"

"github.com/di-wu/parser/ast"
)

func ExampleString() {
p, _ := ast.New([]byte("\"2819c223-7f76-453a-919d-413861904646\""))
fmt.Println(String(p))
fmt.Println(String(doubleQuote)(p))
// Output:
// ["String","\"2819c223-7f76-453a-919d-413861904646\""] <nil>
}

func ExampleString_complex() {
p, _ := ast.New([]byte("\"W/\\\"990-6468886345120203448\\\"\""))
fmt.Println(String(p))
fmt.Println(String(doubleQuote)(p))
// Output:
// ["String","\"W/\\\"990-6468886345120203448\\\"\""] <nil>
}

func ExampleStringSingleQuote() {
p, _ := ast.New([]byte("'2819c223-7f76-453a-919d-413861904646'"))
fmt.Println(String(singleQuote)(p))
// Output:
// ["String","'2819c223-7f76-453a-919d-413861904646'"] <nil>
}

func ExampleStringSingleQuote_complex() {
p, _ := ast.New([]byte("'W/\\\"990-6468886345120203448\\\"'"))
fmt.Println(String(singleQuote)(p))
// Output:
// ["String","'W/\\\"990-6468886345120203448\\\"'"] <nil>
}
2 changes: 1 addition & 1 deletion internal/spec/grammar.pegn
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Digits <-- [0-9]+
Frac <-- '.' Digits
Int <-- '0' / [1-9] [0-9]*

String <-- '"' Character* '"'
String <-- '"' Character* '"' / "'" Character* "'"
Character <- Unescaped / '\' Escaped
Unescaped <- [x20-x21] / [x23-x5B] / [x5D-x10FFFF]
Escaped <- '"'
Expand Down
6 changes: 4 additions & 2 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ const (
Frac
Int

String
StringWithDoubleQuotes
StringWithSingleQuotes

URI
)
Expand Down Expand Up @@ -67,7 +68,8 @@ var Stringer = []string{
"Frac",
"Int",

"String",
"StringWithDoubleQuotes",
"StringWithSingleQuotes",

"URI",
}
Loading