mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-01-12 03:21:33 +00:00
Fix bug with special chars handling in strings, detect package key queries,
arch condition for dependency-like queries.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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: ""})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user