From 3aeeafe79eceb85c47086c75c62cc99029f4c323 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 21 May 2021 18:12:32 +0300 Subject: [PATCH] [#3] policy: use ANTLRv4 parser generator Signed-off-by: Evgenii Stratonikov --- .gitattributes | 4 + go.mod | 2 +- go.sum | 5 +- pkg/policy/doc.go | 2 +- pkg/policy/grammar.ebnf | 55 ---- pkg/policy/grammar.go | 60 ---- pkg/policy/parser/Query.g4 | 45 +++ pkg/policy/parser/Query.interp | Bin 0 -> 4174 bytes pkg/policy/parser/Query.tokens | Bin 0 -> 331 bytes pkg/policy/parser/QueryLexer.g4 | 41 +++ pkg/policy/parser/QueryLexer.interp | Bin 0 -> 7060 bytes pkg/policy/parser/QueryLexer.tokens | Bin 0 -> 331 bytes pkg/policy/parser/generate.go | 3 + pkg/policy/parser/query_base_listener.go | Bin 0 -> 4408 bytes pkg/policy/parser/query_base_visitor.go | Bin 0 -> 1712 bytes pkg/policy/parser/query_lexer.go | Bin 0 -> 9729 bytes pkg/policy/parser/query_listener.go | Bin 0 -> 3058 bytes pkg/policy/parser/query_parser.go | Bin 0 -> 59362 bytes pkg/policy/parser/query_visitor.go | Bin 0 -> 1645 bytes pkg/policy/query.go | 363 +++++++++++++++-------- pkg/policy/query_test.go | 35 ++- 21 files changed, 374 insertions(+), 241 deletions(-) create mode 100644 .gitattributes delete mode 100644 pkg/policy/grammar.ebnf delete mode 100644 pkg/policy/grammar.go create mode 100644 pkg/policy/parser/Query.g4 create mode 100644 pkg/policy/parser/Query.interp create mode 100644 pkg/policy/parser/Query.tokens create mode 100644 pkg/policy/parser/QueryLexer.g4 create mode 100644 pkg/policy/parser/QueryLexer.interp create mode 100644 pkg/policy/parser/QueryLexer.tokens create mode 100644 pkg/policy/parser/generate.go create mode 100644 pkg/policy/parser/query_base_listener.go create mode 100644 pkg/policy/parser/query_base_visitor.go create mode 100644 pkg/policy/parser/query_lexer.go create mode 100644 pkg/policy/parser/query_listener.go create mode 100644 pkg/policy/parser/query_parser.go create mode 100644 pkg/policy/parser/query_visitor.go diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..b371347c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +/pkg/policy/parser/*.go -diff +/pkg/policy/parser/generate.go diff +**/*.interp -diff +**/*.tokens -diff diff --git a/go.mod b/go.mod index f7f18292..25c51c6b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/nspcc-dev/neofs-sdk-go go 1.16 require ( - github.com/alecthomas/participle v0.7.1 + github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1 github.com/nspcc-dev/neofs-api-go v1.27.0 github.com/stretchr/testify v1.6.1 go.uber.org/zap v1.10.0 diff --git a/go.sum b/go.sum index 799b7f75..ed17a369 100644 --- a/go.sum +++ b/go.sum @@ -10,15 +10,14 @@ github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3 github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/alecthomas/participle v0.7.1 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+BwZB5xWs= -github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY= -github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1 h1:zFRi26YWd7NIorBXe8UkevRl0dIvk/AnXHWaAiZG+Yk= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= diff --git a/pkg/policy/doc.go b/pkg/policy/doc.go index e2f94bb7..10394fa1 100644 --- a/pkg/policy/doc.go +++ b/pkg/policy/doc.go @@ -1,5 +1,5 @@ // Package policy provides facilities for creating policy from SQL-like language. -// eBNF grammar is provided in `grammar.ebnf` for illustration. +// ANTLRv4 grammar is provided in `parser/Query.g4` and `parser/QueryLexer.g4`. // // Current limitations: // 1. Grouping filter expressions in parenthesis is not supported right now. diff --git a/pkg/policy/grammar.ebnf b/pkg/policy/grammar.ebnf deleted file mode 100644 index 2bcd3a20..00000000 --- a/pkg/policy/grammar.ebnf +++ /dev/null @@ -1,55 +0,0 @@ -Policy ::= - RepStmt, [RepStmt], - CbtStmt?, - [SelectStmt], - [FilterStmt], -; - -RepStmt ::= - 'REP', Number1, (* number of object replicas *) - ('AS', Ident)? (* optional selector name *) -; - -CbtStmt ::= 'CBF', Number1 (* container backup factor *) -; - -SelectStmt ::= - 'SELECT', Number1, (* number of nodes to select without container backup factor *) - ('IN', Clause?, Ident)?, (* bucket name *) - FROM, (Ident | '*'), (* filter reference or whole netmap *) - ('AS', Ident)? (* optional selector name *) -; - -Clause ::= - 'SAME' (* nodes from the same bucket *) - | 'DISTINCT' (* nodes from distinct buckets *) -; - -FilterStmt ::= - 'FILTER', AndChain, ['OR', AndChain], - 'AS', Ident (* obligatory filter name *) -; - -AndChain ::= - Expr, ['AND', Expr] -; - -Expr ::= - '@' Ident (* filter reference *) - | Key, Op, Value (* attribute filter *) -; - -Op ::= 'EQ' | 'NE' | 'GE' | 'GT' | 'LT' | 'LE' -; - -Key ::= Ident | String -; - -Value ::= Ident | Number | String -; - -Number1 ::= Digit1 [Digit]; -Number ::= Digit [Digit]; - -Digit1 ::= '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ; -Digit ::= '0' | Digit1; diff --git a/pkg/policy/grammar.go b/pkg/policy/grammar.go deleted file mode 100644 index 6fd228e7..00000000 --- a/pkg/policy/grammar.go +++ /dev/null @@ -1,60 +0,0 @@ -//nolint:govet,golint // fails on struct tags here, but participle needs this syntax -package policy - -import ( - "github.com/alecthomas/participle" -) - -var parser *participle.Parser - -func init() { - p, err := participle.Build(&query{}) - if err != nil { - panic(err) - } - parser = p -} - -type query struct { - Replicas []*replicaStmt `@@+` - CBF uint32 `("CBF" @Int)?` - Selectors []*selectorStmt `@@*` - Filters []*filterStmt `@@*` -} - -type replicaStmt struct { - Count int `"REP" @Int` - Selector string `("IN" @Ident)?` -} - -type selectorStmt struct { - Count uint32 `"SELECT" @Int` - Bucket []string `("IN" @(("SAME" | "DISTINCT")? Ident))?` - Filter string `"FROM" @(Ident | "*")` - Name string `("AS" @Ident)?` -} - -type filterStmt struct { - Value *orChain `"FILTER" @@` - Name string `"AS" @Ident` -} - -type filterOrExpr struct { - Reference string `"@"@Ident` - Expr *simpleExpr `| @@` -} - -type orChain struct { - Clauses []*andChain `@@ ("OR" @@)*` -} - -type andChain struct { - Clauses []*filterOrExpr `@@ ("AND" @@)*` -} - -type simpleExpr struct { - Key string `@(Ident | String)` - // We don't use literals here to improve error messages. - Op string `@Ident` - Value string `@(Ident | String | Int)` -} diff --git a/pkg/policy/parser/Query.g4 b/pkg/policy/parser/Query.g4 new file mode 100644 index 00000000..0cad576e --- /dev/null +++ b/pkg/policy/parser/Query.g4 @@ -0,0 +1,45 @@ +parser grammar Query; + +options { + tokenVocab = QueryLexer; +} + +policy: repStmt+ cbfStmt? selectStmt* filterStmt*; + +repStmt: + REP Count = NUMBER1 // number of object replicas + (IN Selector = ident)?; // optional selector name + +cbfStmt: CBF BackupFactor = NUMBER1; // container backup factor + +selectStmt: + SELECT Count = NUMBER1 // number of nodes to select without container backup factor *) + (IN clause? Bucket = ident)? // bucket name + FROM Filter = identWC // filter reference or whole netmap + (AS Name = ident)? // optional selector name + ; + +clause: CLAUSE_SAME | CLAUSE_DISTINCT; // nodes from distinct buckets + +filterExpr: + F1 = filterExpr Op = AND_OP F2 = filterExpr + | F1 = filterExpr Op = OR_OP F2 = filterExpr + | expr + ; + +filterStmt: + FILTER Expr = filterExpr + AS Name = ident // obligatory filter name + ; + +expr: + AT Filter = ident // reference to named filter + | Key = filterKey SIMPLE_OP Value = filterValue // attribute comparison + ; + +filterKey : ident | STRING; +filterValue : ident | number | STRING; +number : ZERO | NUMBER1; +keyword : REP | IN | AS | SELECT | FROM | FILTER; +ident : keyword | IDENT; +identWC : ident | WILDCARD; diff --git a/pkg/policy/parser/Query.interp b/pkg/policy/parser/Query.interp new file mode 100644 index 0000000000000000000000000000000000000000..63f74d9a5f7db8596c48f2d0c52c4bffcbc36599 GIT binary patch literal 4174 zcmb7`TW=dT5QX3QEA-~J@WWDf<*ACT6hdv;kdzu|Q5d$f0gT8O$TC|0`+jGJS}o}y zK!FuGb9iRv47u9f-P5=Fv9r0~)!Um*=keyDe)$j{UpJd@G+mq?g`B)3?8cxop zpI5VM3ESIyb>Zy#a=M%?!ZOX%b(o*d7VEJ1eExB^jN#AO@*=F(%lYEBaJ33y`?{(3 z@}8yjEo|%O)$U;z?ry)@{8De~yPZRuo7a~*e7)bW*|UG2x5>1{`r!XwzonHwZZ@wq zyZ>-oZ^O6x?VqRZmvH~3e%$$ZbrM3j**$&;fAx+#-To*JjywJCXwpCK^m^4u-e@ox z9e3hzGz5%;UJtNW4JKm(y5q5!So~sFbnN5K#71AjW)YhU+YOl5&L5i1$P$&n8L=sV z&FF#HT-dQPOOw3l9+NyTu1Ow+iP0pFpFAdcEK)Ht`y&gC9MjzG4Uz;eZHhNd_vw|E`7kH48D$7jP{sVhBr0_mz^k~C7fDo zU@{gqF4u?!mG(tC!S|62*#B#U%r#E?fvH2(L^E1Q`+-Sj?us9bP+9%Kh+w6wbUf&H zX4=HmkfvBAHTxybgTy+PG?7SNv_TPR$xm4wh{&%s`QfN7MKi@{V%!nY_fwM?`q}?W z`43Bn5)qYnWWr!)Ogez&RtI2Rg1cZ{SY7dj?N4sc_2goSO)DaoSw*%CH09kYz%J+m zB59$4C^g{MhNwL79dB4tJR-VHv*Ry9)zPG=pzdQ;C{fF?I`rd;h>Yndo6_2pm=5Xy zQYWfdTacQLN}Pczq#SLqCy8LxZ*tgP71G5yJXgHB`9(K|HJK7mez86FXyy+>T~;6{ zzc|H480LQ@PbMCIzpP7hdY}=G#VDSRuGj=|^n7dYf#oWWSy*2=S~rONexXIVN-cfb z>C+1;bTBATAf6VoglfGktmfcmJYi)c?q@VomHKp(59oyq6hjA>`~gzH?BC;{7RUxD z+%bGTv$^b!B1SBx0du>wVTI}xnbEW>UIDGC2>yU(R28uabRdf*Vh91A{us^^FeGUf zJfy!Np@72e8Zzk}f<{IAUWlZHb{WGUW*Gjk=uZBS^h~Y2krtKDqJ_cCF#KV0qVk6{ z$&#MUNw)w@m0?{ERlJe*0S;6^XPQrqT##bLZWlKUAj5Ddl|gi^&njTApj7zj#c{y2 z|E@&RT_Dy7X`-|2DFlpJ$cnHuMxP#QQf`-kvA5T7hVfg8#>gtqFJ;QDD^s1P6%WB3i0TWE> zq@HmEObAu6hKR2ZVTi4pQAm0Vdp<=%na~f>_O7)I0_+Q;h=-(aXO&kQNK^*Q2_3K~ zx?}jUVqFMRx_=F_#O+q?4JaM&L2~9G>Ek;Pihb^=n3Ve|$4c$$Gl&5DelThFYK;{! zd1itwB1e_3IO$lC&%T<Q@uUD8o{K52uv?qbe Q>VwViB0DVWwcGvsKMaQe`v3p{ literal 0 HcmV?d00001 diff --git a/pkg/policy/parser/Query.tokens b/pkg/policy/parser/Query.tokens new file mode 100644 index 0000000000000000000000000000000000000000..7f5aee960e912a0eb93d1254eb0be4dcdb88ee23 GIT binary patch literal 331 zcmXw!!D_=m3`Fnx5w$&8cACToK{sBxEUb5p)*;Y~|Nk#KCipZXghp>diF;jDsQRHj zwY03+4nL6FsuxN{zbQ1m()5`1M$NKKo8D>my68xA%A2`LN3WFQ6g;X3O*uT9?JX>( z(w}2YZA%5$GEm;v;8s*Foe6_^{9=khQYl{@{JSCx_4=BfN@kAF>jAr2)n z8gO++0R}Y#n^R$cb7%~720H_u-}DT8KKL00_}u^*9q|_gSqC>9;!b2t1Mvq0*`E+( L|3Hx4K}Pxq{%=e0 literal 0 HcmV?d00001 diff --git a/pkg/policy/parser/QueryLexer.g4 b/pkg/policy/parser/QueryLexer.g4 new file mode 100644 index 00000000..6c245b69 --- /dev/null +++ b/pkg/policy/parser/QueryLexer.g4 @@ -0,0 +1,41 @@ +lexer grammar QueryLexer; + +AND_OP : 'AND'; +OR_OP : 'OR'; +SIMPLE_OP : 'EQ' | 'NE' | 'GE' | 'GT' | 'LT' | 'LE'; + +REP : 'REP'; +IN : 'IN'; +AS : 'AS'; +CBF : 'CBF'; +SELECT : 'SELECT'; +FROM : 'FROM'; +FILTER : 'FILTER'; +WILDCARD : '*'; + +CLAUSE_SAME : 'SAME'; +CLAUSE_DISTINCT : 'DISTINCT'; + +L_PAREN : '('; +R_PAREN : ')'; +AT : '@'; + +IDENT : Nondigit (Digit | Nondigit)* ; +fragment Digit : [0-9] ; +fragment Nondigit : [a-zA-Z_] ; + +NUMBER1 : [1-9] Digit* ; +ZERO : '0' ; + +// Taken from antlr4 json grammar with minor corrections. +// https://github.com/antlr/grammars-v4/blob/master/json/JSON.g4 +STRING : '"' (ESC | SAFECODEPOINTDOUBLE)* '"' + | '\'' (ESC | SAFECODEPOINTSINGLE)* '\'' ; + +fragment ESC : '\\' (['"\\/bfnrt] | UNICODE) ; +fragment UNICODE : 'u' HEX HEX HEX HEX ; +fragment HEX : [0-9a-fA-F] ; +fragment SAFECODEPOINTSINGLE : ~ ['\\\u0000-\u001F] ; +fragment SAFECODEPOINTDOUBLE : ~ ["\\\u0000-\u001F] ; + +WS : [ \t\n\r] + -> skip ; diff --git a/pkg/policy/parser/QueryLexer.interp b/pkg/policy/parser/QueryLexer.interp new file mode 100644 index 0000000000000000000000000000000000000000..d453ff6e05930c0ed8b54aaca3ea4cceb2712edd GIT binary patch literal 7060 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 L|3Hx4K}Pxq{%=e0 literal 0 HcmV?d00001 diff --git a/pkg/policy/parser/generate.go b/pkg/policy/parser/generate.go new file mode 100644 index 00000000..850f3621 --- /dev/null +++ b/pkg/policy/parser/generate.go @@ -0,0 +1,3 @@ +package parser + +//go:generate antlr4 -Dlanguage=Go -visitor QueryLexer.g4 Query.g4 diff --git a/pkg/policy/parser/query_base_listener.go b/pkg/policy/parser/query_base_listener.go new file mode 100644 index 0000000000000000000000000000000000000000..d872ae1da61b8c0f331f306fba2f297e90dca5f2 GIT binary patch literal 4408 zcmb7{KX2PG5XE^g0p{VC@N)>)cd)!$ z!Q%R66$U|_q_4@2L7ni1afl*37z9~SS6slwE)y^9I!vn~N=lJ){G3O;Ek#za=x2pH z7eOEoe@_~wm2a~~C_|7nkbpd0=S(oj`|WMT<-S%0!I?C`t4*6q_tGzV55%Enj*gAs zZ7Tl$3xcu$mI@Dlb5ul+z&6Je?AjHCy*o08AFL4L zu&upD^hD%I+njs@4!#3YQ$P}&nIJpB0^ zfNzhA=8L6&JDe&_4!bGmC@(Z7qLm9 zkc_BMJTt<&5uyj78e#}UbfO` zg4U1~1}^Mw+Ak1wBQ_nE5~3f)u8b8{2^b$?`g@59^rJSqlHxoT<^)q$Ct4XinF+{S zO9dBrxpoktv83P__4AU0ta3ut&7AxN+G4XawQ-UUfv6-a2Fiy()D9~YX3&Nl2uvz& zTXd)B>Oj!5tZuJ=acan6LkvC-xJAa(iA`e@7hHcLX9= pw$9LB5?2R;Zio1#DRv12|z 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 Rvlp(emiBEhgKYgP{sGV$F9!es literal 0 HcmV?d00001 diff --git a/pkg/policy/parser/query_parser.go b/pkg/policy/parser/query_parser.go new file mode 100644 index 0000000000000000000000000000000000000000..fb94f3a2573c6d6ebacf2689b0f89022a44144b7 GIT binary patch literal 59362 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 literal 0 HcmV?d00001 diff --git a/pkg/policy/parser/query_visitor.go b/pkg/policy/parser/query_visitor.go new file mode 100644 index 0000000000000000000000000000000000000000..b6128a568b159afe615ceacbf2a9247a419c1433 GIT binary patch literal 1645 zcmb`H!EVAZ42I9-DJ*u`c4(!E%Z{k932g!fXq-dpvWT{+LaI?A-hFA@mL4WeQ#iEp zZzca{yJ^A@A{8U-F-Oe}#*k>0!AFU@3icz|RWP1!-d8XRo`a_VCJUG^HZYycHUVP= z7YDvaD7ZeNhA^a!jLED}+Q4ux%~!b#gvvsmn^e<#6zVcJGQ;pyQD(@P-8=@nut?D$ ze9EIVN<(5FPX1stT9j6CDQum#GOCi9sz8d{T{1VQ6D|v6Vr??h`mab5pb?$~u}k`dh$Imj z;Ym1Rio*0E>n=l_tM){Rl$S?rrxiKXwBlk<`$VSp>`YIEZV@kChA>xs6I6d_WL;^X^8O)q>nzcnj}s)K>2-w^7f38~sYSupdo8KNtW2 literal 0 HcmV?d00001 diff --git a/pkg/policy/query.go b/pkg/policy/query.go index 1b6447ef..7fbdae58 100644 --- a/pkg/policy/query.go +++ b/pkg/policy/query.go @@ -3,9 +3,12 @@ package policy import ( "errors" "fmt" + "strconv" "strings" + "github.com/antlr/antlr4/runtime/Go/antlr" "github.com/nspcc-dev/neofs-api-go/pkg/netmap" + "github.com/nspcc-dev/neofs-sdk-go/pkg/policy/parser" ) var ( @@ -19,83 +22,252 @@ var ( ErrUnknownFilter = errors.New("policy: filter not found") // ErrUnknownSelector is returned when a value of IN is unknown. ErrUnknownSelector = errors.New("policy: selector not found") + // ErrSyntaxError is returned for errors found by ANTLR parser. + ErrSyntaxError = errors.New("policy: syntax error") ) -func parse(s string) (*query, error) { - q := new(query) - err := parser.Parse(strings.NewReader(s), q) - if err != nil { - return nil, err - } - return q, nil +type policyVisitor struct { + errors []error + parser.BaseQueryVisitor + antlr.DefaultErrorListener } // Parse parses s into a placement policy. func Parse(s string) (*netmap.PlacementPolicy, error) { - q, err := parse(s) - if err != nil { + return parse(s) +} + +func newPolicyVisitor() *policyVisitor { + return &policyVisitor{} +} + +func parse(s string) (*netmap.PlacementPolicy, error) { + input := antlr.NewInputStream(s) + lexer := parser.NewQueryLexer(input) + stream := antlr.NewCommonTokenStream(lexer, 0) + + p := parser.NewQuery(stream) + p.BuildParseTrees = true + + v := newPolicyVisitor() + p.RemoveErrorListeners() + p.AddErrorListener(v) + pl := p.Policy().Accept(v) + + if len(v.errors) != 0 { + return nil, v.errors[0] + } + if err := validatePolicy(pl.(*netmap.PlacementPolicy)); err != nil { return nil, err } + return pl.(*netmap.PlacementPolicy), nil +} - seenFilters := map[string]bool{} - fs := make([]*netmap.Filter, 0, len(q.Filters)) - for _, qf := range q.Filters { - f, err := filterFromOrChain(qf.Value, seenFilters) - if err != nil { - return nil, err +func (p *policyVisitor) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) { + p.reportError(fmt.Errorf("%w: line %d:%d %s", ErrSyntaxError, line, column, msg)) +} + +func (p *policyVisitor) reportError(err error) interface{} { + p.errors = append(p.errors, err) + return nil +} + +// VisitPolicy implements parser.QueryVisitor interface. +func (p *policyVisitor) VisitPolicy(ctx *parser.PolicyContext) interface{} { + if len(p.errors) != 0 { + return nil + } + + pl := new(netmap.PlacementPolicy) + + repStmts := ctx.AllRepStmt() + rs := make([]*netmap.Replica, 0, len(repStmts)) + for _, r := range repStmts { + res, ok := r.Accept(p).(*netmap.Replica) + if !ok { + return nil } - f.SetName(qf.Name) - fs = append(fs, f) - seenFilters[qf.Name] = true + + rs = append(rs, res) + } + pl.SetReplicas(rs...) + + if cbfStmt := ctx.CbfStmt(); cbfStmt != nil { + cbf, ok := cbfStmt.(*parser.CbfStmtContext).Accept(p).(uint32) + if !ok { + return nil + } + pl.SetContainerBackupFactor(cbf) + } + + selStmts := ctx.AllSelectStmt() + ss := make([]*netmap.Selector, 0, len(selStmts)) + for _, s := range selStmts { + res, ok := s.Accept(p).(*netmap.Selector) + if !ok { + return nil + } + + ss = append(ss, res) + } + pl.SetSelectors(ss...) + + filtStmts := ctx.AllFilterStmt() + fs := make([]*netmap.Filter, 0, len(filtStmts)) + for _, f := range filtStmts { + fs = append(fs, f.Accept(p).(*netmap.Filter)) + } + pl.SetFilters(fs...) + + return pl +} + +func (p *policyVisitor) VisitCbfStmt(ctx *parser.CbfStmtContext) interface{} { + cbf, err := strconv.ParseUint(ctx.GetBackupFactor().GetText(), 10, 32) + if err != nil { + return p.reportError(ErrInvalidNumber) + } + + return uint32(cbf) +} + +// VisitRepStmt implements parser.QueryVisitor interface. +func (p *policyVisitor) VisitRepStmt(ctx *parser.RepStmtContext) interface{} { + num, err := strconv.ParseUint(ctx.GetCount().GetText(), 10, 32) + if err != nil { + return p.reportError(ErrInvalidNumber) + } + + rs := new(netmap.Replica) + rs.SetCount(uint32(num)) + + if sel := ctx.GetSelector(); sel != nil { + rs.SetSelector(sel.GetText()) + } + + return rs +} + +// VisitSelectStmt implements parser.QueryVisitor interface. +func (p *policyVisitor) VisitSelectStmt(ctx *parser.SelectStmtContext) interface{} { + res, err := strconv.ParseUint(ctx.GetCount().GetText(), 10, 32) + if err != nil { + return p.reportError(ErrInvalidNumber) + } + + s := new(netmap.Selector) + s.SetCount(uint32(res)) + + if clStmt := ctx.Clause(); clStmt != nil { + s.SetClause(clauseFromString(clStmt.GetText())) + } + + if bStmt := ctx.GetBucket(); bStmt != nil { + s.SetAttribute(ctx.GetBucket().GetText()) + } + + s.SetFilter(ctx.GetFilter().GetText()) // either ident or wildcard + + if ctx.AS() != nil { + s.SetName(ctx.GetName().GetText()) + } + return s +} + +// VisitFilterStmt implements parser.QueryVisitor interface. +func (p *policyVisitor) VisitFilterStmt(ctx *parser.FilterStmtContext) interface{} { + f := p.VisitFilterExpr(ctx.GetExpr().(*parser.FilterExprContext)).(*netmap.Filter) + f.SetName(ctx.GetName().GetText()) + return f +} + +func (p *policyVisitor) VisitFilterExpr(ctx *parser.FilterExprContext) interface{} { + if eCtx := ctx.Expr(); eCtx != nil { + return eCtx.Accept(p) + } + + f := new(netmap.Filter) + op := operationFromString(ctx.GetOp().GetText()) + f.SetOperation(op) + + f1 := ctx.GetF1().Accept(p).(*netmap.Filter) + f2 := ctx.GetF2().Accept(p).(*netmap.Filter) + + // Consider f1=(.. AND ..) AND f2. This can be merged because our AND operation + // is of arbitrary arity. ANTLR generates left-associative parse-tree by default. + if f1.Operation() == op { + f.SetInnerFilters(append(f1.InnerFilters(), f2)...) + return f + } + + f.SetInnerFilters(f1, f2) + return f +} + +// VisitFilterKey implements parser.QueryVisitor interface. +func (p *policyVisitor) VisitFilterKey(ctx *parser.FilterKeyContext) interface{} { + if id := ctx.Ident(); id != nil { + return id.GetText() + } + + str := ctx.STRING().GetText() + return str[1 : len(str)-1] +} + +func (p *policyVisitor) VisitFilterValue(ctx *parser.FilterValueContext) interface{} { + if id := ctx.Ident(); id != nil { + return id.GetText() + } + + if num := ctx.Number(); num != nil { + return num.GetText() + } + + str := ctx.STRING().GetText() + return str[1 : len(str)-1] +} + +// VisitExpr implements parser.QueryVisitor interface. +func (p *policyVisitor) VisitExpr(ctx *parser.ExprContext) interface{} { + f := new(netmap.Filter) + if flt := ctx.GetFilter(); flt != nil { + f.SetName(flt.GetText()) + return f + } + + key := ctx.GetKey().Accept(p) + opStr := ctx.SIMPLE_OP().GetText() + value := ctx.GetValue().Accept(p) + + f.SetKey(key.(string)) + f.SetOperation(operationFromString(opStr)) + f.SetValue(value.(string)) + return f +} + +// validatePolicy checks high-level constraints such as filter link in SELECT +// being actually defined in FILTER section. +func validatePolicy(p *netmap.PlacementPolicy) error { + seenFilters := map[string]bool{} + for _, f := range p.Filters() { + seenFilters[f.Name()] = true } seenSelectors := map[string]bool{} - ss := make([]*netmap.Selector, 0, len(q.Selectors)) - for _, qs := range q.Selectors { - if qs.Filter != netmap.MainFilterName && !seenFilters[qs.Filter] { - return nil, fmt.Errorf("%w: '%s'", ErrUnknownFilter, qs.Filter) + for _, s := range p.Selectors() { + if flt := s.Filter(); flt != netmap.MainFilterName && !seenFilters[flt] { + return fmt.Errorf("%w: '%s'", ErrUnknownFilter, flt) } - s := netmap.NewSelector() - switch len(qs.Bucket) { - case 1: // only bucket - s.SetAttribute(qs.Bucket[0]) - case 2: // clause + bucket - s.SetClause(clauseFromString(qs.Bucket[0])) - s.SetAttribute(qs.Bucket[1]) - } - s.SetName(qs.Name) - seenSelectors[qs.Name] = true - s.SetFilter(qs.Filter) - if qs.Count == 0 { - return nil, fmt.Errorf("%w: SELECT", ErrInvalidNumber) - } - s.SetCount(qs.Count) - ss = append(ss, s) + seenSelectors[s.Name()] = true } - rs := make([]*netmap.Replica, 0, len(q.Replicas)) - for _, qr := range q.Replicas { - r := netmap.NewReplica() - if qr.Selector != "" { - if !seenSelectors[qr.Selector] { - return nil, fmt.Errorf("%w: '%s'", ErrUnknownSelector, qr.Selector) - } - r.SetSelector(qr.Selector) + for _, r := range p.Replicas() { + if sel := r.Selector(); sel != "" && !seenSelectors[sel] { + return fmt.Errorf("%w: '%s'", ErrUnknownSelector, sel) } - if qr.Count == 0 { - return nil, fmt.Errorf("%w: REP", ErrInvalidNumber) - } - r.SetCount(uint32(qr.Count)) - rs = append(rs, r) } - p := new(netmap.PlacementPolicy) - p.SetFilters(fs...) - p.SetSelectors(ss...) - p.SetReplicas(rs...) - p.SetContainerBackupFactor(q.CBF) - - return p, nil + return nil } func clauseFromString(s string) netmap.Clause { @@ -105,74 +277,31 @@ func clauseFromString(s string) netmap.Clause { case "DISTINCT": return netmap.ClauseDistinct default: - return 0 + // Such errors should be handled by ANTLR code thus this panic. + panic(fmt.Errorf("BUG: invalid clause: %s", s)) } } -func filterFromOrChain(expr *orChain, seen map[string]bool) (*netmap.Filter, error) { - var fs []*netmap.Filter - for _, ac := range expr.Clauses { - f, err := filterFromAndChain(ac, seen) - if err != nil { - return nil, err - } - fs = append(fs, f) - } - if len(fs) == 1 { - return fs[0], nil - } - - f := netmap.NewFilter() - f.SetOperation(netmap.OpOR) - f.SetInnerFilters(fs...) - return f, nil -} - -func filterFromAndChain(expr *andChain, seen map[string]bool) (*netmap.Filter, error) { - var fs []*netmap.Filter - for _, fe := range expr.Clauses { - var f *netmap.Filter - var err error - if fe.Expr != nil { - f, err = filterFromSimpleExpr(fe.Expr, seen) - } else { - f = netmap.NewFilter() - f.SetName(fe.Reference) - } - if err != nil { - return nil, err - } - fs = append(fs, f) - } - if len(fs) == 1 { - return fs[0], nil - } - - f := netmap.NewFilter() - f.SetOperation(netmap.OpAND) - f.SetInnerFilters(fs...) - return f, nil -} - -func filterFromSimpleExpr(se *simpleExpr, seen map[string]bool) (*netmap.Filter, error) { - f := netmap.NewFilter() - f.SetKey(se.Key) - switch se.Op { +func operationFromString(op string) netmap.Operation { + switch strings.ToUpper(op) { + case "AND": + return netmap.OpAND + case "OR": + return netmap.OpOR case "EQ": - f.SetOperation(netmap.OpEQ) + return netmap.OpEQ case "NE": - f.SetOperation(netmap.OpNE) + return netmap.OpNE case "GE": - f.SetOperation(netmap.OpGE) + return netmap.OpGE case "GT": - f.SetOperation(netmap.OpGT) + return netmap.OpGT case "LE": - f.SetOperation(netmap.OpLE) + return netmap.OpLE case "LT": - f.SetOperation(netmap.OpLT) + return netmap.OpLT default: - return nil, fmt.Errorf("%w: '%s'", ErrUnknownOp, se.Op) + // Such errors should be handled by ANTLR code thus this panic. + panic(fmt.Errorf("BUG: invalid operation: %s", op)) } - f.SetValue(se.Value) - return f, nil } diff --git a/pkg/policy/query_test.go b/pkg/policy/query_test.go index ebe2be29..2493edc2 100644 --- a/pkg/policy/query_test.go +++ b/pkg/policy/query_test.go @@ -2,6 +2,7 @@ package policy import ( "errors" + "fmt" "testing" "github.com/nspcc-dev/neofs-api-go/v2/netmap" @@ -80,6 +81,32 @@ func TestFromSelectNoAttribute(t *testing.T) { }) } +func TestString(t *testing.T) { + qTemplate := `REP 1 +SELECT 1 IN City FROM Filt +FILTER Property EQ %s AND Something NE 7 AS Filt` + + testCases := []string{ + `"double-quoted"`, + `"with ' single"`, + `'single-quoted'`, + `'with " double'`, + } + + for _, s := range testCases { + t.Run(s, func(t *testing.T) { + q := fmt.Sprintf(qTemplate, s) + r, err := Parse(q) + require.NoError(t, err) + + expected := newFilter("Filt", "", "", netmap.AND, + newFilter("", "Property", s[1:len(s)-1], netmap.EQ), + newFilter("", "Something", "7", netmap.NE)) + require.EqualValues(t, []*netmap.Filter{expected}, r.Filters()) + }) + } +} + func TestFromSelectClause(t *testing.T) { q := `REP 4 SELECT 3 IN Country FROM * @@ -205,12 +232,12 @@ func TestValidation(t *testing.T) { SELECT 1 IN City FROM F FILTER Country KEK RU AS F` _, err := Parse(q) - require.True(t, errors.Is(err, ErrUnknownOp), "got: %v", err) + require.True(t, errors.Is(err, ErrSyntaxError), "got: %v", err) }) t.Run("TypoInREP", func(t *testing.T) { q := `REK 3` _, err := Parse(q) - require.Error(t, err) + require.True(t, errors.Is(err, ErrSyntaxError)) }) t.Run("InvalidFilterName", func(t *testing.T) { q := `REP 3 @@ -223,13 +250,13 @@ func TestValidation(t *testing.T) { t.Run("InvalidNumberInREP", func(t *testing.T) { q := `REP 0` _, err := Parse(q) - require.True(t, errors.Is(err, ErrInvalidNumber), "got: %v", err) + require.True(t, errors.Is(err, ErrSyntaxError), "got: %v", err) }) t.Run("InvalidNumberInREP", func(t *testing.T) { q := `REP 1 IN Good SELECT 0 IN City FROM *` _, err := Parse(q) - require.True(t, errors.Is(err, ErrInvalidNumber), "got: %v", err) + require.True(t, errors.Is(err, ErrSyntaxError), "got: %v", err) }) }