Fix bug with special chars handling in strings, detect package key queries,

arch condition for dependency-like queries.
This commit is contained in:
Andrey Smirnov
2014-07-12 00:14:49 +04:00
parent b42fd71acf
commit d54ef1e921
5 changed files with 88 additions and 8 deletions

View File

@@ -28,6 +28,8 @@ const (
itemEq // =
itemPatMatch // %
itemRegexp // ~
itemLeftCurly // {
itemRightCurly // }
itemString
)
@@ -152,6 +154,10 @@ func lexMain(l *lexer) stateFn {
l.emit(itemLeftParen)
case r == ')':
l.emit(itemRightParen)
case r == '{':
l.emit(itemLeftCurly)
case r == '}':
l.emit(itemRightCurly)
case r == '|':
l.emit(itemOr)
case r == ',':
@@ -195,7 +201,7 @@ func lexMain(l *lexer) stateFn {
func lexString(l *lexer) stateFn {
for {
r := l.next()
if unicode.IsSpace(r) || strings.IndexRune("()|,!<>=%~", r) > 0 {
if unicode.IsSpace(r) || strings.IndexRune("()|,!{}", r) > 0 {
l.backup()
l.emit(itemString)
return lexMain(l)

View File

@@ -11,7 +11,7 @@ type LexerSuite struct {
var _ = Suite(&LexerSuite{})
func (s *LexerSuite) TestLexing(c *C) {
_, ch := lex("query", "package (<< 1.3), $Source | !app")
_, ch := lex("query", "package (<< 1.3), $Source | !app, data {i386}")
c.Check(<-ch, Equals, item{typ: itemString, val: "package"})
c.Check(<-ch, Equals, item{typ: itemLeftParen, val: "("})
@@ -23,6 +23,11 @@ func (s *LexerSuite) TestLexing(c *C) {
c.Check(<-ch, Equals, item{typ: itemOr, val: "|"})
c.Check(<-ch, Equals, item{typ: itemNot, val: "!"})
c.Check(<-ch, Equals, item{typ: itemString, val: "app"})
c.Check(<-ch, Equals, item{typ: itemAnd, val: ","})
c.Check(<-ch, Equals, item{typ: itemString, val: "data"})
c.Check(<-ch, Equals, item{typ: itemLeftCurly, val: "{"})
c.Check(<-ch, Equals, item{typ: itemString, val: "i386"})
c.Check(<-ch, Equals, item{typ: itemRightCurly, val: "}"})
c.Check(<-ch, Equals, item{typ: itemEOF, val: ""})
}

View File

@@ -1,6 +1,10 @@
// Package query implements query language for
package query
import (
"github.com/smira/aptly/deb"
)
/*
Query language resembling Debian dependencies and reprepro
@@ -10,8 +14,16 @@ package query
A := B | B ',' A
B := C | '!' B
C := '(' Query ')' | D
D := <field> <condition>
D := <field> <condition> <arch_condition> | <pkg>_<version>_<arch>
field := <package-name> | <field> | $special_field
condition := '(' <operator> value ')' |
arch_condition := '{' arch '}' |
operator := | << | < | <= | > | >> | >= | = | % | ~
*/
// Parse parses input package query into PackageQuery tree ready for evaluation
func Parse(query string) (result deb.PackageQuery, err error) {
l, _ := lex("", query)
result, err = parse(l)
return
}

View File

@@ -106,7 +106,21 @@ func operatorToRelation(operator itemType) int {
panic("unable to map token to relation")
}
// D := <field> <condition>
// isPackageRef returns ok true if field has format pkg_version_arch
func parsePackageRef(query string) (pkg, version, arch string, ok bool) {
i := strings.Index(query, "_")
if i != -1 {
pkg, query = query[:i], query[i+1:]
j := strings.LastIndex(query, "_")
if j != -1 {
version, arch = query[:j], query[j+1:]
ok = true
}
}
return
}
// D := <field> <condition> <arch_condition> | <package>_<version>_<arch>
// field := <package-name> | <field> | $special_field
func (p *parser) D() deb.PackageQuery {
if p.input.Current().typ != itemString {
@@ -122,10 +136,19 @@ func (p *parser) D() deb.PackageQuery {
if strings.HasPrefix(field, "$") || unicode.IsUpper(r) {
// special field or regular field
return &deb.FieldQuery{Field: field, Relation: operatorToRelation(operator), Value: value}
} else if operator == 0 && value == "" {
if pkg, version, arch, ok := parsePackageRef(field); ok {
// query for specific package
return &deb.PkgQuery{Pkg: pkg, Version: version, Arch: arch}
}
}
// regular dependency-like query
return &deb.DependencyQuery{Dep: deb.Dependency{Pkg: field, Relation: operatorToRelation(operator), Version: value}}
return &deb.DependencyQuery{Dep: deb.Dependency{
Pkg: field,
Relation: operatorToRelation(operator),
Version: value,
Architecture: p.ArchCondition()}}
}
// condition := '(' <operator> value ')' |
@@ -162,3 +185,24 @@ func (p *parser) Condition() (operator itemType, value string) {
return
}
// arch_condition := '{' arch '}' |
func (p *parser) ArchCondition() (arch string) {
if p.input.Current().typ != itemLeftCurly {
return
}
p.input.Consume()
if p.input.Current().typ != itemString {
panic(fmt.Sprintf("unexpected token %s: expecting architecture", p.input.Current()))
}
arch = p.input.Current().val
p.input.Consume()
if p.input.Current().typ != itemRightCurly {
panic(fmt.Sprintf("unexpected token %s: expecting '}'", p.input.Current()))
}
p.input.Consume()
return
}

View File

@@ -11,11 +11,11 @@ type SyntaxSuite struct {
var _ = Suite(&SyntaxSuite{})
func (s *SyntaxSuite) TestParsing(c *C) {
l, _ := lex("query", "package (<< 1.3), $Source")
l, _ := lex("query", "package (<< 1.3~dev), $Source")
q, err := parse(l)
c.Assert(err, IsNil)
c.Check(q.(*deb.AndQuery).L, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionLess, Version: "1.3"}})
c.Check(q.(*deb.AndQuery).L, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionLess, Version: "1.3~dev"}})
c.Check(q.(*deb.AndQuery).R, DeepEquals, &deb.FieldQuery{Field: "$Source"})
l, _ = lex("query", "package (1.3), Name (lala) | !$Source")
@@ -39,6 +39,19 @@ func (s *SyntaxSuite) TestParsing(c *C) {
c.Assert(err, IsNil)
c.Check(q, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionGreaterOrEqual, Version: "5.3.7"}})
l, _ = lex("query", "alien-data_1.3.4~dev_i386")
q, err = parse(l)
c.Assert(err, IsNil)
c.Check(q, DeepEquals, &deb.PkgQuery{Pkg: "alien-data", Version: "1.3.4~dev", Arch: "i386"})
l, _ = lex("query", "package (> 5.3.7) {amd64}")
q, err = parse(l)
c.Assert(err, IsNil)
c.Check(q, DeepEquals, &deb.DependencyQuery{
Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionGreaterOrEqual, Version: "5.3.7", Architecture: "amd64"}})
}
func (s *SyntaxSuite) TestParsingErrors(c *C) {
@@ -48,7 +61,7 @@ func (s *SyntaxSuite) TestParsingErrors(c *C) {
l, _ = lex("query", "package>5.3.7)")
_, err = parse(l)
c.Check(err, ErrorMatches, "parsing failed: unexpected token >: expecting end of query")
c.Check(err, ErrorMatches, "parsing failed: unexpected token \\): expecting end of query")
l, _ = lex("query", "package | !|")
_, err = parse(l)