From 30794dd24eef6841bd69c5ab682de89fd86269ff Mon Sep 17 00:00:00 2001 From: Mauro Valota Date: Thu, 9 Dec 2021 09:32:34 +0100 Subject: [PATCH] feat: added string patterns --- README.md | 9 +- entrypoint.sh | 2 + grammar/ApiFilter.g4 | 27 +- .../{Operator.php => ComparisonOperator.php} | 3 +- src/ApiFilter/Filter/Pattern.php | 75 +++ src/ApiFilter/Filter/PatternOperator.php | 11 + src/ApiFilter/FilterFactory.php | 52 +- src/ApiFilter/Parser/ApiFilter.interp | 24 +- src/ApiFilter/Parser/ApiFilter.tokens | 38 +- src/ApiFilter/Parser/ApiFilterBaseVisitor.php | 35 +- src/ApiFilter/Parser/ApiFilterLexer.interp | 30 +- src/ApiFilter/Parser/ApiFilterLexer.php | 310 +++++++----- src/ApiFilter/Parser/ApiFilterLexer.tokens | 38 +- src/ApiFilter/Parser/ApiFilterParser.php | 454 ++++++++++++------ src/ApiFilter/Parser/ApiFilterVisitor.php | 34 +- tests/ApiFilter/Filter/ComparisonTest.php | 33 +- tests/ApiFilter/Filter/ConjunctionTest.php | 12 +- tests/ApiFilter/Filter/DisjunctionTest.php | 12 +- tests/ApiFilter/Filter/FilterTest.php | 6 +- tests/ApiFilter/Filter/PatternTest.php | 87 ++++ tests/ApiFilter/FilterFactoryTest.php | 293 ++++++++--- 21 files changed, 1141 insertions(+), 444 deletions(-) rename src/ApiFilter/Filter/{Operator.php => ComparisonOperator.php} (78%) create mode 100644 src/ApiFilter/Filter/Pattern.php create mode 100644 src/ApiFilter/Filter/PatternOperator.php create mode 100644 tests/ApiFilter/Filter/PatternTest.php diff --git a/README.md b/README.md index 9d24e8a..2b55201 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ The **op** is one of the following (unquoted): - _Less than_: '<' - _Less than or equal to_: '<=' - _Not equal_: '<>', '!=' -- _Like_: 'like', 'LIKE' The **value** can be one of: - _String_: 'value' @@ -48,6 +47,12 @@ The **value** can be one of: - _Int_: 46 - _Double_: 12.34 +It is also possible to **match a String against a Pattern** using one of those operators: +- _Like_: 'like', 'LIKE' +- _Contains_: 'contains', 'CONTAINS' +- _Starts With_: 'starts with', 'STARTS WITH' +- _Ends With_: 'ends with', 'ENDS WITH' + Additionally, it is possible to check if field has a value or not, using NULL: - _Null field_: 'IS NULL', 'is null', '= null', '= NULL' - _Not Null field_: 'IS NOT NULL', 'is not null', '<> null', '!= null', '<> NULL', '!= NULL' @@ -60,7 +65,7 @@ Parenthesis can be used to compose complex expressions. For example: ``` -city = 'Bergamo' and (age < 30 or (dev = true and (name = 'Giorgio' and name is not null))) +city = 'Bergamo' and (age < 30 or (dev = true and (name = 'Giorgio' and surname is not null) or employer starts with 'Fatture')) ``` ## Testing diff --git a/entrypoint.sh b/entrypoint.sh index c3e3d1b..4b211b5 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,4 +3,6 @@ wget https://www.antlr.org/download/antlr-4.9.2-complete.jar export CLASSPATH=".:./antlr-4.9.2-complete.jar:$CLASSPATH" +rm -rf /parser/* + java -jar ./antlr-4.9.2-complete.jar -Dlanguage=PHP /grammar/ApiFilter.g4 -o /parser -package FattureInCloud\\ApiFilter\\Parser -visitor -no-listener \ No newline at end of file diff --git a/grammar/ApiFilter.g4 b/grammar/ApiFilter.g4 index b212131..07e93e4 100644 --- a/grammar/ApiFilter.g4 +++ b/grammar/ApiFilter.g4 @@ -8,22 +8,26 @@ filter: expression EOF; expression: condition # conditionExp + | pattern # patternExp | OPEN_PAR expression CLOSE_PAR # parenthesisExp | expression AND expression # conjunctionExp | expression OR expression # disjunctionExp; condition: - comparison # comparisonCondition - | emptyfield # emptyCondition + comparison # comparisonCondition + | emptyfield # emptyCondition | filledfield # filledCondition; -comparison: FIELD op value; +comparison: FIELD comparisonop value; emptyfield: FIELD (EQ | IS) NULL; filledfield: FIELD (NEQ | IS NOT) NULL; -op: EQ | GT | GTE | LT | LTE | NEQ | LIKE; +comparisonop: EQ | GT | GTE | LT | LTE | NEQ; value: (BOOL | STRING | integer | decimal); +pattern: FIELD patternop STRING; +patternop: LIKE | CONTAINS | STARTSWITH | ENDSWITH; + integer: INT; decimal: INT DOT INT; @@ -37,26 +41,35 @@ GTE: '>='; LT: '<'; LTE: '<='; NEQ: ('<>' | '!='); + LIKE: ('like' | 'LIKE'); +CONTAINS: ('contains' | 'CONTAINS'); +STARTSWITH: STARTS ' '* WITH; +ENDSWITH: ENDS ' '* WITH; + +STARTS: ('starts' | 'STARTS'); +ENDS: ('ends' | 'ENDS'); +WITH: ('with' | 'WITH'); BOOL: ('true' | 'false'); STRING: '\'' ( ~'\'' | '\'\'')+ '\''; AND: ('and' | 'AND'); OR: ('or' | 'OR'); -NOT: ('not' | 'NOT'); IS: ('is' | 'IS'); NULL: ('null' | 'NULL'); +NOT: ('not' | 'NOT'); OPEN_PAR: '('; CLOSE_PAR: ')'; +INT: (DIGIT)+; + DOT: '.'; -INT: (DIGIT)+; +FIELD: LOWERCASE (( LOWERCASE | '_' | DOT)* LOWERCASE)?; -FIELD: LOWERCASE (( LOWERCASE | '_' | '.')* LOWERCASE)?; fragment LOWERCASE: [a-z]; fragment UPPERCASE: [A-Z]; diff --git a/src/ApiFilter/Filter/Operator.php b/src/ApiFilter/Filter/ComparisonOperator.php similarity index 78% rename from src/ApiFilter/Filter/Operator.php rename to src/ApiFilter/Filter/ComparisonOperator.php index 613d423..2b4c79b 100644 --- a/src/ApiFilter/Filter/Operator.php +++ b/src/ApiFilter/Filter/ComparisonOperator.php @@ -2,7 +2,7 @@ namespace FattureInCloud\ApiFilter\Filter; -abstract class Operator +abstract class ComparisonOperator { const EQ = "="; const GT = ">"; @@ -10,5 +10,4 @@ abstract class Operator const LT = "<"; const LTE = "<="; const NEQ = "<>"; - const LIKE = "like"; } diff --git a/src/ApiFilter/Filter/Pattern.php b/src/ApiFilter/Filter/Pattern.php new file mode 100644 index 0000000..e87140d --- /dev/null +++ b/src/ApiFilter/Filter/Pattern.php @@ -0,0 +1,75 @@ +field = $field; + $this->op = $op; + $this->value = $value; + } + + /** + * @return string + */ + public function getField(): string + { + return $this->field; + } + + /** + * @param string $field + */ + public function setField(string $field): void + { + $this->field = $field; + } + + /** + * @return string + */ + public function getOp(): string + { + return $this->op; + } + + /** + * @param string $op + */ + public function setOp(string $op): void + { + $this->op = $op; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * @param mixed $value + */ + public function setValue($value): void + { + $this->value = $value; + } + + public function __toString(): string + { + return $this->field . " " . $this->op . " " . $this->value; + } +} diff --git a/src/ApiFilter/Filter/PatternOperator.php b/src/ApiFilter/Filter/PatternOperator.php new file mode 100644 index 0000000..f4da4d8 --- /dev/null +++ b/src/ApiFilter/Filter/PatternOperator.php @@ -0,0 +1,11 @@ +visit($context->condition()); } + public function visitPatternExp(Context\PatternExpContext $context) + { + return $this->visit($context->pattern()); + } + public function visitParenthesisExp(ParenthesisExpContext $context): Expression { return $this->visit($context->expression()); @@ -81,7 +88,7 @@ public function visitComparisonCondition(Context\ComparisonConditionContext $con public function visitComparison(Context\ComparisonContext $context): Comparison { $field = $context->FIELD()->getText(); - $op = $this->visit($context->op()); + $op = $this->visit($context->comparisonop()); $value = $this->visit($context->value()); return new Comparison($field, $op, $value); } @@ -111,23 +118,21 @@ public function visitDecimal(DecimalContext $context) return floatval($context->getText()); } - public function visitOp(OpContext $context) + public function visitComparisonop(ComparisonopContext $context) { $operator = null; if ($context->EQ()) { - $operator = Operator::EQ; + $operator = ComparisonOperator::EQ; } elseif ($context->GT()) { - $operator = Operator::GT; + $operator = ComparisonOperator::GT; } elseif ($context->GTE()) { - $operator = Operator::GTE; + $operator = ComparisonOperator::GTE; } elseif ($context->LT()) { - $operator = Operator::LT; + $operator = ComparisonOperator::LT; } elseif ($context->LTE()) { - $operator = Operator::LTE; + $operator = ComparisonOperator::LTE; } elseif ($context->NEQ()) { - $operator = Operator::NEQ; - } elseif ($context->LIKE()) { - $operator = Operator::LIKE; + $operator = ComparisonOperator::NEQ; } return $operator; } @@ -153,4 +158,27 @@ public function visitFilledfield(Context\FilledfieldContext $context): FilledFie $field = $context->FIELD()->getText(); return new FilledField($field); } + + public function visitPattern(Context\PatternContext $context) + { + $field = $context->FIELD()->getText(); + $op = $this->visit($context->patternop()); + $value = substr($context->STRING()->getText(), 1, -1); + return new Pattern($field, $op, $value); + } + + public function visitPatternop(Context\PatternopContext $context) + { + $op = null; + if ($context->LIKE()) { + $op = PatternOperator::LIKE; + } elseif ($context->CONTAINS()) { + $op = PatternOperator::CONTAINS; + } elseif ($context->STARTSWITH()) { + $op = PatternOperator::STARTS_WITH; + } elseif ($context->ENDSWITH()) { + $op = PatternOperator::ENDS_WITH; + } + return $op; + } } diff --git a/src/ApiFilter/Parser/ApiFilter.interp b/src/ApiFilter/Parser/ApiFilter.interp index aab946b..e9f8ffc 100644 --- a/src/ApiFilter/Parser/ApiFilter.interp +++ b/src/ApiFilter/Parser/ApiFilter.interp @@ -14,10 +14,16 @@ null null null null +null +null +null +null +null +null '(' ')' -'.' null +'.' null null @@ -30,17 +36,23 @@ LT LTE NEQ LIKE +CONTAINS +STARTSWITH +ENDSWITH +STARTS +ENDS +WITH BOOL STRING AND OR -NOT IS NULL +NOT OPEN_PAR CLOSE_PAR -DOT INT +DOT FIELD WS @@ -51,11 +63,13 @@ condition comparison emptyfield filledfield -op +comparisonop value +pattern +patternop integer decimal atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 22, 80, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 32, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 40, 10, 3, 12, 3, 14, 3, 43, 11, 3, 3, 4, 3, 4, 3, 4, 5, 4, 48, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 62, 10, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 72, 10, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 2, 3, 4, 12, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 2, 4, 4, 2, 3, 3, 15, 15, 3, 2, 3, 9, 2, 78, 2, 22, 3, 2, 2, 2, 4, 31, 3, 2, 2, 2, 6, 47, 3, 2, 2, 2, 8, 49, 3, 2, 2, 2, 10, 53, 3, 2, 2, 2, 12, 57, 3, 2, 2, 2, 14, 65, 3, 2, 2, 2, 16, 71, 3, 2, 2, 2, 18, 73, 3, 2, 2, 2, 20, 75, 3, 2, 2, 2, 22, 23, 5, 4, 3, 2, 23, 24, 7, 2, 2, 3, 24, 3, 3, 2, 2, 2, 25, 26, 8, 3, 1, 2, 26, 32, 5, 6, 4, 2, 27, 28, 7, 17, 2, 2, 28, 29, 5, 4, 3, 2, 29, 30, 7, 18, 2, 2, 30, 32, 3, 2, 2, 2, 31, 25, 3, 2, 2, 2, 31, 27, 3, 2, 2, 2, 32, 41, 3, 2, 2, 2, 33, 34, 12, 4, 2, 2, 34, 35, 7, 12, 2, 2, 35, 40, 5, 4, 3, 5, 36, 37, 12, 3, 2, 2, 37, 38, 7, 13, 2, 2, 38, 40, 5, 4, 3, 4, 39, 33, 3, 2, 2, 2, 39, 36, 3, 2, 2, 2, 40, 43, 3, 2, 2, 2, 41, 39, 3, 2, 2, 2, 41, 42, 3, 2, 2, 2, 42, 5, 3, 2, 2, 2, 43, 41, 3, 2, 2, 2, 44, 48, 5, 8, 5, 2, 45, 48, 5, 10, 6, 2, 46, 48, 5, 12, 7, 2, 47, 44, 3, 2, 2, 2, 47, 45, 3, 2, 2, 2, 47, 46, 3, 2, 2, 2, 48, 7, 3, 2, 2, 2, 49, 50, 7, 21, 2, 2, 50, 51, 5, 14, 8, 2, 51, 52, 5, 16, 9, 2, 52, 9, 3, 2, 2, 2, 53, 54, 7, 21, 2, 2, 54, 55, 9, 2, 2, 2, 55, 56, 7, 16, 2, 2, 56, 11, 3, 2, 2, 2, 57, 61, 7, 21, 2, 2, 58, 62, 7, 8, 2, 2, 59, 60, 7, 15, 2, 2, 60, 62, 7, 14, 2, 2, 61, 58, 3, 2, 2, 2, 61, 59, 3, 2, 2, 2, 62, 63, 3, 2, 2, 2, 63, 64, 7, 16, 2, 2, 64, 13, 3, 2, 2, 2, 65, 66, 9, 3, 2, 2, 66, 15, 3, 2, 2, 2, 67, 72, 7, 10, 2, 2, 68, 72, 7, 11, 2, 2, 69, 72, 5, 18, 10, 2, 70, 72, 5, 20, 11, 2, 71, 67, 3, 2, 2, 2, 71, 68, 3, 2, 2, 2, 71, 69, 3, 2, 2, 2, 71, 70, 3, 2, 2, 2, 72, 17, 3, 2, 2, 2, 73, 74, 7, 20, 2, 2, 74, 19, 3, 2, 2, 2, 75, 76, 7, 20, 2, 2, 76, 77, 7, 19, 2, 2, 77, 78, 7, 20, 2, 2, 78, 21, 3, 2, 2, 2, 8, 31, 39, 41, 47, 61, 71] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 28, 91, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 37, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 45, 10, 3, 12, 3, 14, 3, 48, 11, 3, 3, 4, 3, 4, 3, 4, 5, 4, 53, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 67, 10, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 77, 10, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 2, 3, 4, 14, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 2, 5, 4, 2, 3, 3, 20, 20, 3, 2, 3, 8, 3, 2, 9, 12, 2, 88, 2, 26, 3, 2, 2, 2, 4, 36, 3, 2, 2, 2, 6, 52, 3, 2, 2, 2, 8, 54, 3, 2, 2, 2, 10, 58, 3, 2, 2, 2, 12, 62, 3, 2, 2, 2, 14, 70, 3, 2, 2, 2, 16, 76, 3, 2, 2, 2, 18, 78, 3, 2, 2, 2, 20, 82, 3, 2, 2, 2, 22, 84, 3, 2, 2, 2, 24, 86, 3, 2, 2, 2, 26, 27, 5, 4, 3, 2, 27, 28, 7, 2, 2, 3, 28, 3, 3, 2, 2, 2, 29, 30, 8, 3, 1, 2, 30, 37, 5, 6, 4, 2, 31, 37, 5, 18, 10, 2, 32, 33, 7, 23, 2, 2, 33, 34, 5, 4, 3, 2, 34, 35, 7, 24, 2, 2, 35, 37, 3, 2, 2, 2, 36, 29, 3, 2, 2, 2, 36, 31, 3, 2, 2, 2, 36, 32, 3, 2, 2, 2, 37, 46, 3, 2, 2, 2, 38, 39, 12, 4, 2, 2, 39, 40, 7, 18, 2, 2, 40, 45, 5, 4, 3, 5, 41, 42, 12, 3, 2, 2, 42, 43, 7, 19, 2, 2, 43, 45, 5, 4, 3, 4, 44, 38, 3, 2, 2, 2, 44, 41, 3, 2, 2, 2, 45, 48, 3, 2, 2, 2, 46, 44, 3, 2, 2, 2, 46, 47, 3, 2, 2, 2, 47, 5, 3, 2, 2, 2, 48, 46, 3, 2, 2, 2, 49, 53, 5, 8, 5, 2, 50, 53, 5, 10, 6, 2, 51, 53, 5, 12, 7, 2, 52, 49, 3, 2, 2, 2, 52, 50, 3, 2, 2, 2, 52, 51, 3, 2, 2, 2, 53, 7, 3, 2, 2, 2, 54, 55, 7, 27, 2, 2, 55, 56, 5, 14, 8, 2, 56, 57, 5, 16, 9, 2, 57, 9, 3, 2, 2, 2, 58, 59, 7, 27, 2, 2, 59, 60, 9, 2, 2, 2, 60, 61, 7, 21, 2, 2, 61, 11, 3, 2, 2, 2, 62, 66, 7, 27, 2, 2, 63, 67, 7, 8, 2, 2, 64, 65, 7, 20, 2, 2, 65, 67, 7, 22, 2, 2, 66, 63, 3, 2, 2, 2, 66, 64, 3, 2, 2, 2, 67, 68, 3, 2, 2, 2, 68, 69, 7, 21, 2, 2, 69, 13, 3, 2, 2, 2, 70, 71, 9, 3, 2, 2, 71, 15, 3, 2, 2, 2, 72, 77, 7, 16, 2, 2, 73, 77, 7, 17, 2, 2, 74, 77, 5, 22, 12, 2, 75, 77, 5, 24, 13, 2, 76, 72, 3, 2, 2, 2, 76, 73, 3, 2, 2, 2, 76, 74, 3, 2, 2, 2, 76, 75, 3, 2, 2, 2, 77, 17, 3, 2, 2, 2, 78, 79, 7, 27, 2, 2, 79, 80, 5, 20, 11, 2, 80, 81, 7, 17, 2, 2, 81, 19, 3, 2, 2, 2, 82, 83, 9, 4, 2, 2, 83, 21, 3, 2, 2, 2, 84, 85, 7, 25, 2, 2, 85, 23, 3, 2, 2, 2, 86, 87, 7, 25, 2, 2, 87, 88, 7, 26, 2, 2, 88, 89, 7, 25, 2, 2, 89, 25, 3, 2, 2, 2, 8, 36, 44, 46, 52, 66, 76] \ No newline at end of file diff --git a/src/ApiFilter/Parser/ApiFilter.tokens b/src/ApiFilter/Parser/ApiFilter.tokens index 5d0034b..eb9e16e 100644 --- a/src/ApiFilter/Parser/ApiFilter.tokens +++ b/src/ApiFilter/Parser/ApiFilter.tokens @@ -5,24 +5,30 @@ LT=4 LTE=5 NEQ=6 LIKE=7 -BOOL=8 -STRING=9 -AND=10 -OR=11 -NOT=12 -IS=13 -NULL=14 -OPEN_PAR=15 -CLOSE_PAR=16 -DOT=17 -INT=18 -FIELD=19 -WS=20 +CONTAINS=8 +STARTSWITH=9 +ENDSWITH=10 +STARTS=11 +ENDS=12 +WITH=13 +BOOL=14 +STRING=15 +AND=16 +OR=17 +IS=18 +NULL=19 +NOT=20 +OPEN_PAR=21 +CLOSE_PAR=22 +INT=23 +DOT=24 +FIELD=25 +WS=26 '='=1 '>'=2 '>='=3 '<'=4 '<='=5 -'('=15 -')'=16 -'.'=17 +'('=21 +')'=22 +'.'=24 diff --git a/src/ApiFilter/Parser/ApiFilterBaseVisitor.php b/src/ApiFilter/Parser/ApiFilterBaseVisitor.php index cc9ff9a..adf5045 100644 --- a/src/ApiFilter/Parser/ApiFilterBaseVisitor.php +++ b/src/ApiFilter/Parser/ApiFilterBaseVisitor.php @@ -25,6 +25,17 @@ public function visitFilter(Context\FilterContext $context) return $this->visitChildren($context); } + /** + * {@inheritdoc} + * + * The default implementation returns the result of calling + * {@see self::visitChildren()} on `context`. + */ + public function visitPatternExp(Context\PatternExpContext $context) + { + return $this->visitChildren($context); + } + /** * {@inheritdoc} * @@ -141,7 +152,7 @@ public function visitFilledfield(Context\FilledfieldContext $context) * The default implementation returns the result of calling * {@see self::visitChildren()} on `context`. */ - public function visitOp(Context\OpContext $context) + public function visitComparisonop(Context\ComparisonopContext $context) { return $this->visitChildren($context); } @@ -157,6 +168,28 @@ public function visitValue(Context\ValueContext $context) return $this->visitChildren($context); } + /** + * {@inheritdoc} + * + * The default implementation returns the result of calling + * {@see self::visitChildren()} on `context`. + */ + public function visitPattern(Context\PatternContext $context) + { + return $this->visitChildren($context); + } + + /** + * {@inheritdoc} + * + * The default implementation returns the result of calling + * {@see self::visitChildren()} on `context`. + */ + public function visitPatternop(Context\PatternopContext $context) + { + return $this->visitChildren($context); + } + /** * {@inheritdoc} * diff --git a/src/ApiFilter/Parser/ApiFilterLexer.interp b/src/ApiFilter/Parser/ApiFilterLexer.interp index cdde470..277d8d4 100644 --- a/src/ApiFilter/Parser/ApiFilterLexer.interp +++ b/src/ApiFilter/Parser/ApiFilterLexer.interp @@ -14,10 +14,16 @@ null null null null +null +null +null +null +null +null '(' ')' -'.' null +'.' null null @@ -30,17 +36,23 @@ LT LTE NEQ LIKE +CONTAINS +STARTSWITH +ENDSWITH +STARTS +ENDS +WITH BOOL STRING AND OR -NOT IS NULL +NOT OPEN_PAR CLOSE_PAR -DOT INT +DOT FIELD WS @@ -52,17 +64,23 @@ LT LTE NEQ LIKE +CONTAINS +STARTSWITH +ENDSWITH +STARTS +ENDS +WITH BOOL STRING AND OR -NOT IS NULL +NOT OPEN_PAR CLOSE_PAR -DOT INT +DOT FIELD LOWERCASE UPPERCASE @@ -77,4 +95,4 @@ mode names: DEFAULT_MODE atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 22, 171, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 66, 10, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 76, 10, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 87, 10, 9, 3, 10, 3, 10, 3, 10, 3, 10, 6, 10, 93, 10, 10, 13, 10, 14, 10, 94, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 105, 10, 11, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 111, 10, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 119, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 5, 14, 125, 10, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 135, 10, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 6, 19, 144, 10, 19, 13, 19, 14, 19, 145, 3, 20, 3, 20, 3, 20, 7, 20, 151, 10, 20, 12, 20, 14, 20, 154, 11, 20, 3, 20, 5, 20, 157, 10, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 6, 24, 166, 10, 24, 13, 24, 14, 24, 167, 3, 24, 3, 24, 2, 2, 25, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 2, 43, 2, 45, 2, 47, 22, 3, 2, 8, 3, 2, 41, 41, 4, 2, 48, 48, 97, 97, 3, 2, 99, 124, 3, 2, 67, 92, 3, 2, 50, 59, 5, 2, 11, 12, 15, 15, 34, 34, 2, 182, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 3, 49, 3, 2, 2, 2, 5, 51, 3, 2, 2, 2, 7, 53, 3, 2, 2, 2, 9, 56, 3, 2, 2, 2, 11, 58, 3, 2, 2, 2, 13, 65, 3, 2, 2, 2, 15, 75, 3, 2, 2, 2, 17, 86, 3, 2, 2, 2, 19, 88, 3, 2, 2, 2, 21, 104, 3, 2, 2, 2, 23, 110, 3, 2, 2, 2, 25, 118, 3, 2, 2, 2, 27, 124, 3, 2, 2, 2, 29, 134, 3, 2, 2, 2, 31, 136, 3, 2, 2, 2, 33, 138, 3, 2, 2, 2, 35, 140, 3, 2, 2, 2, 37, 143, 3, 2, 2, 2, 39, 147, 3, 2, 2, 2, 41, 158, 3, 2, 2, 2, 43, 160, 3, 2, 2, 2, 45, 162, 3, 2, 2, 2, 47, 165, 3, 2, 2, 2, 49, 50, 7, 63, 2, 2, 50, 4, 3, 2, 2, 2, 51, 52, 7, 64, 2, 2, 52, 6, 3, 2, 2, 2, 53, 54, 7, 64, 2, 2, 54, 55, 7, 63, 2, 2, 55, 8, 3, 2, 2, 2, 56, 57, 7, 62, 2, 2, 57, 10, 3, 2, 2, 2, 58, 59, 7, 62, 2, 2, 59, 60, 7, 63, 2, 2, 60, 12, 3, 2, 2, 2, 61, 62, 7, 62, 2, 2, 62, 66, 7, 64, 2, 2, 63, 64, 7, 35, 2, 2, 64, 66, 7, 63, 2, 2, 65, 61, 3, 2, 2, 2, 65, 63, 3, 2, 2, 2, 66, 14, 3, 2, 2, 2, 67, 68, 7, 110, 2, 2, 68, 69, 7, 107, 2, 2, 69, 70, 7, 109, 2, 2, 70, 76, 7, 103, 2, 2, 71, 72, 7, 78, 2, 2, 72, 73, 7, 75, 2, 2, 73, 74, 7, 77, 2, 2, 74, 76, 7, 71, 2, 2, 75, 67, 3, 2, 2, 2, 75, 71, 3, 2, 2, 2, 76, 16, 3, 2, 2, 2, 77, 78, 7, 118, 2, 2, 78, 79, 7, 116, 2, 2, 79, 80, 7, 119, 2, 2, 80, 87, 7, 103, 2, 2, 81, 82, 7, 104, 2, 2, 82, 83, 7, 99, 2, 2, 83, 84, 7, 110, 2, 2, 84, 85, 7, 117, 2, 2, 85, 87, 7, 103, 2, 2, 86, 77, 3, 2, 2, 2, 86, 81, 3, 2, 2, 2, 87, 18, 3, 2, 2, 2, 88, 92, 7, 41, 2, 2, 89, 93, 10, 2, 2, 2, 90, 91, 7, 41, 2, 2, 91, 93, 7, 41, 2, 2, 92, 89, 3, 2, 2, 2, 92, 90, 3, 2, 2, 2, 93, 94, 3, 2, 2, 2, 94, 92, 3, 2, 2, 2, 94, 95, 3, 2, 2, 2, 95, 96, 3, 2, 2, 2, 96, 97, 7, 41, 2, 2, 97, 20, 3, 2, 2, 2, 98, 99, 7, 99, 2, 2, 99, 100, 7, 112, 2, 2, 100, 105, 7, 102, 2, 2, 101, 102, 7, 67, 2, 2, 102, 103, 7, 80, 2, 2, 103, 105, 7, 70, 2, 2, 104, 98, 3, 2, 2, 2, 104, 101, 3, 2, 2, 2, 105, 22, 3, 2, 2, 2, 106, 107, 7, 113, 2, 2, 107, 111, 7, 116, 2, 2, 108, 109, 7, 81, 2, 2, 109, 111, 7, 84, 2, 2, 110, 106, 3, 2, 2, 2, 110, 108, 3, 2, 2, 2, 111, 24, 3, 2, 2, 2, 112, 113, 7, 112, 2, 2, 113, 114, 7, 113, 2, 2, 114, 119, 7, 118, 2, 2, 115, 116, 7, 80, 2, 2, 116, 117, 7, 81, 2, 2, 117, 119, 7, 86, 2, 2, 118, 112, 3, 2, 2, 2, 118, 115, 3, 2, 2, 2, 119, 26, 3, 2, 2, 2, 120, 121, 7, 107, 2, 2, 121, 125, 7, 117, 2, 2, 122, 123, 7, 75, 2, 2, 123, 125, 7, 85, 2, 2, 124, 120, 3, 2, 2, 2, 124, 122, 3, 2, 2, 2, 125, 28, 3, 2, 2, 2, 126, 127, 7, 112, 2, 2, 127, 128, 7, 119, 2, 2, 128, 129, 7, 110, 2, 2, 129, 135, 7, 110, 2, 2, 130, 131, 7, 80, 2, 2, 131, 132, 7, 87, 2, 2, 132, 133, 7, 78, 2, 2, 133, 135, 7, 78, 2, 2, 134, 126, 3, 2, 2, 2, 134, 130, 3, 2, 2, 2, 135, 30, 3, 2, 2, 2, 136, 137, 7, 42, 2, 2, 137, 32, 3, 2, 2, 2, 138, 139, 7, 43, 2, 2, 139, 34, 3, 2, 2, 2, 140, 141, 7, 48, 2, 2, 141, 36, 3, 2, 2, 2, 142, 144, 5, 45, 23, 2, 143, 142, 3, 2, 2, 2, 144, 145, 3, 2, 2, 2, 145, 143, 3, 2, 2, 2, 145, 146, 3, 2, 2, 2, 146, 38, 3, 2, 2, 2, 147, 156, 5, 41, 21, 2, 148, 151, 5, 41, 21, 2, 149, 151, 9, 3, 2, 2, 150, 148, 3, 2, 2, 2, 150, 149, 3, 2, 2, 2, 151, 154, 3, 2, 2, 2, 152, 150, 3, 2, 2, 2, 152, 153, 3, 2, 2, 2, 153, 155, 3, 2, 2, 2, 154, 152, 3, 2, 2, 2, 155, 157, 5, 41, 21, 2, 156, 152, 3, 2, 2, 2, 156, 157, 3, 2, 2, 2, 157, 40, 3, 2, 2, 2, 158, 159, 9, 4, 2, 2, 159, 42, 3, 2, 2, 2, 160, 161, 9, 5, 2, 2, 161, 44, 3, 2, 2, 2, 162, 163, 9, 6, 2, 2, 163, 46, 3, 2, 2, 2, 164, 166, 9, 7, 2, 2, 165, 164, 3, 2, 2, 2, 166, 167, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2, 167, 168, 3, 2, 2, 2, 168, 169, 3, 2, 2, 2, 169, 170, 8, 24, 2, 2, 170, 48, 3, 2, 2, 2, 18, 2, 65, 75, 86, 92, 94, 104, 110, 118, 124, 134, 145, 150, 152, 156, 167, 3, 8, 2, 2] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 28, 254, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 78, 10, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 88, 10, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 106, 10, 9, 3, 10, 3, 10, 7, 10, 110, 10, 10, 12, 10, 14, 10, 113, 11, 10, 3, 10, 3, 10, 3, 11, 3, 11, 7, 11, 119, 10, 11, 12, 11, 14, 11, 122, 11, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 138, 10, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 148, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 5, 14, 158, 10, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 169, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 6, 16, 175, 10, 16, 13, 16, 14, 16, 176, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 5, 17, 187, 10, 17, 3, 18, 3, 18, 3, 18, 3, 18, 5, 18, 193, 10, 18, 3, 19, 3, 19, 3, 19, 3, 19, 5, 19, 199, 10, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 5, 20, 209, 10, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 5, 21, 217, 10, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 6, 24, 224, 10, 24, 13, 24, 14, 24, 225, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 7, 26, 234, 10, 26, 12, 26, 14, 26, 237, 11, 26, 3, 26, 5, 26, 240, 10, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 6, 30, 249, 10, 30, 13, 30, 14, 30, 250, 3, 30, 3, 30, 2, 2, 31, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 2, 55, 2, 57, 2, 59, 28, 3, 2, 7, 3, 2, 41, 41, 3, 2, 99, 124, 3, 2, 67, 92, 3, 2, 50, 59, 5, 2, 11, 12, 15, 15, 34, 34, 2, 272, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 3, 61, 3, 2, 2, 2, 5, 63, 3, 2, 2, 2, 7, 65, 3, 2, 2, 2, 9, 68, 3, 2, 2, 2, 11, 70, 3, 2, 2, 2, 13, 77, 3, 2, 2, 2, 15, 87, 3, 2, 2, 2, 17, 105, 3, 2, 2, 2, 19, 107, 3, 2, 2, 2, 21, 116, 3, 2, 2, 2, 23, 137, 3, 2, 2, 2, 25, 147, 3, 2, 2, 2, 27, 157, 3, 2, 2, 2, 29, 168, 3, 2, 2, 2, 31, 170, 3, 2, 2, 2, 33, 186, 3, 2, 2, 2, 35, 192, 3, 2, 2, 2, 37, 198, 3, 2, 2, 2, 39, 208, 3, 2, 2, 2, 41, 216, 3, 2, 2, 2, 43, 218, 3, 2, 2, 2, 45, 220, 3, 2, 2, 2, 47, 223, 3, 2, 2, 2, 49, 227, 3, 2, 2, 2, 51, 229, 3, 2, 2, 2, 53, 241, 3, 2, 2, 2, 55, 243, 3, 2, 2, 2, 57, 245, 3, 2, 2, 2, 59, 248, 3, 2, 2, 2, 61, 62, 7, 63, 2, 2, 62, 4, 3, 2, 2, 2, 63, 64, 7, 64, 2, 2, 64, 6, 3, 2, 2, 2, 65, 66, 7, 64, 2, 2, 66, 67, 7, 63, 2, 2, 67, 8, 3, 2, 2, 2, 68, 69, 7, 62, 2, 2, 69, 10, 3, 2, 2, 2, 70, 71, 7, 62, 2, 2, 71, 72, 7, 63, 2, 2, 72, 12, 3, 2, 2, 2, 73, 74, 7, 62, 2, 2, 74, 78, 7, 64, 2, 2, 75, 76, 7, 35, 2, 2, 76, 78, 7, 63, 2, 2, 77, 73, 3, 2, 2, 2, 77, 75, 3, 2, 2, 2, 78, 14, 3, 2, 2, 2, 79, 80, 7, 110, 2, 2, 80, 81, 7, 107, 2, 2, 81, 82, 7, 109, 2, 2, 82, 88, 7, 103, 2, 2, 83, 84, 7, 78, 2, 2, 84, 85, 7, 75, 2, 2, 85, 86, 7, 77, 2, 2, 86, 88, 7, 71, 2, 2, 87, 79, 3, 2, 2, 2, 87, 83, 3, 2, 2, 2, 88, 16, 3, 2, 2, 2, 89, 90, 7, 101, 2, 2, 90, 91, 7, 113, 2, 2, 91, 92, 7, 112, 2, 2, 92, 93, 7, 118, 2, 2, 93, 94, 7, 99, 2, 2, 94, 95, 7, 107, 2, 2, 95, 96, 7, 112, 2, 2, 96, 106, 7, 117, 2, 2, 97, 98, 7, 69, 2, 2, 98, 99, 7, 81, 2, 2, 99, 100, 7, 80, 2, 2, 100, 101, 7, 86, 2, 2, 101, 102, 7, 67, 2, 2, 102, 103, 7, 75, 2, 2, 103, 104, 7, 80, 2, 2, 104, 106, 7, 85, 2, 2, 105, 89, 3, 2, 2, 2, 105, 97, 3, 2, 2, 2, 106, 18, 3, 2, 2, 2, 107, 111, 5, 23, 12, 2, 108, 110, 7, 34, 2, 2, 109, 108, 3, 2, 2, 2, 110, 113, 3, 2, 2, 2, 111, 109, 3, 2, 2, 2, 111, 112, 3, 2, 2, 2, 112, 114, 3, 2, 2, 2, 113, 111, 3, 2, 2, 2, 114, 115, 5, 27, 14, 2, 115, 20, 3, 2, 2, 2, 116, 120, 5, 25, 13, 2, 117, 119, 7, 34, 2, 2, 118, 117, 3, 2, 2, 2, 119, 122, 3, 2, 2, 2, 120, 118, 3, 2, 2, 2, 120, 121, 3, 2, 2, 2, 121, 123, 3, 2, 2, 2, 122, 120, 3, 2, 2, 2, 123, 124, 5, 27, 14, 2, 124, 22, 3, 2, 2, 2, 125, 126, 7, 117, 2, 2, 126, 127, 7, 118, 2, 2, 127, 128, 7, 99, 2, 2, 128, 129, 7, 116, 2, 2, 129, 130, 7, 118, 2, 2, 130, 138, 7, 117, 2, 2, 131, 132, 7, 85, 2, 2, 132, 133, 7, 86, 2, 2, 133, 134, 7, 67, 2, 2, 134, 135, 7, 84, 2, 2, 135, 136, 7, 86, 2, 2, 136, 138, 7, 85, 2, 2, 137, 125, 3, 2, 2, 2, 137, 131, 3, 2, 2, 2, 138, 24, 3, 2, 2, 2, 139, 140, 7, 103, 2, 2, 140, 141, 7, 112, 2, 2, 141, 142, 7, 102, 2, 2, 142, 148, 7, 117, 2, 2, 143, 144, 7, 71, 2, 2, 144, 145, 7, 80, 2, 2, 145, 146, 7, 70, 2, 2, 146, 148, 7, 85, 2, 2, 147, 139, 3, 2, 2, 2, 147, 143, 3, 2, 2, 2, 148, 26, 3, 2, 2, 2, 149, 150, 7, 121, 2, 2, 150, 151, 7, 107, 2, 2, 151, 152, 7, 118, 2, 2, 152, 158, 7, 106, 2, 2, 153, 154, 7, 89, 2, 2, 154, 155, 7, 75, 2, 2, 155, 156, 7, 86, 2, 2, 156, 158, 7, 74, 2, 2, 157, 149, 3, 2, 2, 2, 157, 153, 3, 2, 2, 2, 158, 28, 3, 2, 2, 2, 159, 160, 7, 118, 2, 2, 160, 161, 7, 116, 2, 2, 161, 162, 7, 119, 2, 2, 162, 169, 7, 103, 2, 2, 163, 164, 7, 104, 2, 2, 164, 165, 7, 99, 2, 2, 165, 166, 7, 110, 2, 2, 166, 167, 7, 117, 2, 2, 167, 169, 7, 103, 2, 2, 168, 159, 3, 2, 2, 2, 168, 163, 3, 2, 2, 2, 169, 30, 3, 2, 2, 2, 170, 174, 7, 41, 2, 2, 171, 175, 10, 2, 2, 2, 172, 173, 7, 41, 2, 2, 173, 175, 7, 41, 2, 2, 174, 171, 3, 2, 2, 2, 174, 172, 3, 2, 2, 2, 175, 176, 3, 2, 2, 2, 176, 174, 3, 2, 2, 2, 176, 177, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, 178, 179, 7, 41, 2, 2, 179, 32, 3, 2, 2, 2, 180, 181, 7, 99, 2, 2, 181, 182, 7, 112, 2, 2, 182, 187, 7, 102, 2, 2, 183, 184, 7, 67, 2, 2, 184, 185, 7, 80, 2, 2, 185, 187, 7, 70, 2, 2, 186, 180, 3, 2, 2, 2, 186, 183, 3, 2, 2, 2, 187, 34, 3, 2, 2, 2, 188, 189, 7, 113, 2, 2, 189, 193, 7, 116, 2, 2, 190, 191, 7, 81, 2, 2, 191, 193, 7, 84, 2, 2, 192, 188, 3, 2, 2, 2, 192, 190, 3, 2, 2, 2, 193, 36, 3, 2, 2, 2, 194, 195, 7, 107, 2, 2, 195, 199, 7, 117, 2, 2, 196, 197, 7, 75, 2, 2, 197, 199, 7, 85, 2, 2, 198, 194, 3, 2, 2, 2, 198, 196, 3, 2, 2, 2, 199, 38, 3, 2, 2, 2, 200, 201, 7, 112, 2, 2, 201, 202, 7, 119, 2, 2, 202, 203, 7, 110, 2, 2, 203, 209, 7, 110, 2, 2, 204, 205, 7, 80, 2, 2, 205, 206, 7, 87, 2, 2, 206, 207, 7, 78, 2, 2, 207, 209, 7, 78, 2, 2, 208, 200, 3, 2, 2, 2, 208, 204, 3, 2, 2, 2, 209, 40, 3, 2, 2, 2, 210, 211, 7, 112, 2, 2, 211, 212, 7, 113, 2, 2, 212, 217, 7, 118, 2, 2, 213, 214, 7, 80, 2, 2, 214, 215, 7, 81, 2, 2, 215, 217, 7, 86, 2, 2, 216, 210, 3, 2, 2, 2, 216, 213, 3, 2, 2, 2, 217, 42, 3, 2, 2, 2, 218, 219, 7, 42, 2, 2, 219, 44, 3, 2, 2, 2, 220, 221, 7, 43, 2, 2, 221, 46, 3, 2, 2, 2, 222, 224, 5, 57, 29, 2, 223, 222, 3, 2, 2, 2, 224, 225, 3, 2, 2, 2, 225, 223, 3, 2, 2, 2, 225, 226, 3, 2, 2, 2, 226, 48, 3, 2, 2, 2, 227, 228, 7, 48, 2, 2, 228, 50, 3, 2, 2, 2, 229, 239, 5, 53, 27, 2, 230, 234, 5, 53, 27, 2, 231, 234, 7, 97, 2, 2, 232, 234, 5, 49, 25, 2, 233, 230, 3, 2, 2, 2, 233, 231, 3, 2, 2, 2, 233, 232, 3, 2, 2, 2, 234, 237, 3, 2, 2, 2, 235, 233, 3, 2, 2, 2, 235, 236, 3, 2, 2, 2, 236, 238, 3, 2, 2, 2, 237, 235, 3, 2, 2, 2, 238, 240, 5, 53, 27, 2, 239, 235, 3, 2, 2, 2, 239, 240, 3, 2, 2, 2, 240, 52, 3, 2, 2, 2, 241, 242, 9, 3, 2, 2, 242, 54, 3, 2, 2, 2, 243, 244, 9, 4, 2, 2, 244, 56, 3, 2, 2, 2, 245, 246, 9, 5, 2, 2, 246, 58, 3, 2, 2, 2, 247, 249, 9, 6, 2, 2, 248, 247, 3, 2, 2, 2, 249, 250, 3, 2, 2, 2, 250, 248, 3, 2, 2, 2, 250, 251, 3, 2, 2, 2, 251, 252, 3, 2, 2, 2, 252, 253, 8, 30, 2, 2, 253, 60, 3, 2, 2, 2, 24, 2, 77, 87, 105, 111, 120, 137, 147, 157, 168, 174, 176, 186, 192, 198, 208, 216, 225, 233, 235, 239, 250, 3, 8, 2, 2] \ No newline at end of file diff --git a/src/ApiFilter/Parser/ApiFilterLexer.php b/src/ApiFilter/Parser/ApiFilterLexer.php index cb691a0..3f9c8ab 100644 --- a/src/ApiFilter/Parser/ApiFilterLexer.php +++ b/src/ApiFilter/Parser/ApiFilterLexer.php @@ -20,9 +20,10 @@ final class ApiFilterLexer extends Lexer { public const EQ = 1, GT = 2, GTE = 3, LT = 4, LTE = 5, NEQ = 6, LIKE = 7, - BOOL = 8, STRING = 9, AND = 10, OR = 11, NOT = 12, IS = 13, - NULL = 14, OPEN_PAR = 15, CLOSE_PAR = 16, DOT = 17, INT = 18, - FIELD = 19, WS = 20; + CONTAINS = 8, STARTSWITH = 9, ENDSWITH = 10, STARTS = 11, + ENDS = 12, WITH = 13, BOOL = 14, STRING = 15, AND = 16, OR = 17, + IS = 18, NULL = 19, NOT = 20, OPEN_PAR = 21, CLOSE_PAR = 22, + INT = 23, DOT = 24, FIELD = 25, WS = 26; /** * @var array @@ -42,8 +43,9 @@ final class ApiFilterLexer extends Lexer * @var array */ public const RULE_NAMES = [ - 'EQ', 'GT', 'GTE', 'LT', 'LTE', 'NEQ', 'LIKE', 'BOOL', 'STRING', 'AND', - 'OR', 'NOT', 'IS', 'NULL', 'OPEN_PAR', 'CLOSE_PAR', 'DOT', 'INT', 'FIELD', + 'EQ', 'GT', 'GTE', 'LT', 'LTE', 'NEQ', 'LIKE', 'CONTAINS', 'STARTSWITH', + 'ENDSWITH', 'STARTS', 'ENDS', 'WITH', 'BOOL', 'STRING', 'AND', 'OR', + 'IS', 'NULL', 'NOT', 'OPEN_PAR', 'CLOSE_PAR', 'INT', 'DOT', 'FIELD', 'LOWERCASE', 'UPPERCASE', 'DIGIT', 'WS' ]; @@ -52,16 +54,18 @@ final class ApiFilterLexer extends Lexer */ private const LITERAL_NAMES = [ null, "'='", "'>'", "'>='", "'<'", "'<='", null, null, null, null, - null, null, null, null, null, "'('", "')'", "'.'" + null, null, null, null, null, null, null, null, null, null, null, + "'('", "')'", null, "'.'" ]; /** * @var array */ private const SYMBOLIC_NAMES = [ - null, "EQ", "GT", "GTE", "LT", "LTE", "NEQ", "LIKE", "BOOL", "STRING", - "AND", "OR", "NOT", "IS", "NULL", "OPEN_PAR", "CLOSE_PAR", "DOT", - "INT", "FIELD", "WS" + null, "EQ", "GT", "GTE", "LT", "LTE", "NEQ", "LIKE", "CONTAINS", "STARTSWITH", + "ENDSWITH", "STARTS", "ENDS", "WITH", "BOOL", "STRING", "AND", "OR", + "IS", "NULL", "NOT", "OPEN_PAR", "CLOSE_PAR", "INT", "DOT", "FIELD", + "WS" ]; /** @@ -69,129 +73,189 @@ final class ApiFilterLexer extends Lexer */ private const SERIALIZED_ATN = "\u{3}\u{608B}\u{A72A}\u{8133}\u{B9ED}\u{417C}\u{3BE7}\u{7786}\u{5964}" . - "\u{2}\u{16}\u{AB}\u{8}\u{1}\u{4}\u{2}\u{9}\u{2}\u{4}\u{3}\u{9}\u{3}" . + "\u{2}\u{1C}\u{FE}\u{8}\u{1}\u{4}\u{2}\u{9}\u{2}\u{4}\u{3}\u{9}\u{3}" . "\u{4}\u{4}\u{9}\u{4}\u{4}\u{5}\u{9}\u{5}\u{4}\u{6}\u{9}\u{6}\u{4}" . "\u{7}\u{9}\u{7}\u{4}\u{8}\u{9}\u{8}\u{4}\u{9}\u{9}\u{9}\u{4}\u{A}" . "\u{9}\u{A}\u{4}\u{B}\u{9}\u{B}\u{4}\u{C}\u{9}\u{C}\u{4}\u{D}\u{9}" . "\u{D}\u{4}\u{E}\u{9}\u{E}\u{4}\u{F}\u{9}\u{F}\u{4}\u{10}\u{9}\u{10}" . "\u{4}\u{11}\u{9}\u{11}\u{4}\u{12}\u{9}\u{12}\u{4}\u{13}\u{9}\u{13}" . "\u{4}\u{14}\u{9}\u{14}\u{4}\u{15}\u{9}\u{15}\u{4}\u{16}\u{9}\u{16}" . - "\u{4}\u{17}\u{9}\u{17}\u{4}\u{18}\u{9}\u{18}\u{3}\u{2}\u{3}\u{2}\u{3}" . + "\u{4}\u{17}\u{9}\u{17}\u{4}\u{18}\u{9}\u{18}\u{4}\u{19}\u{9}\u{19}" . + "\u{4}\u{1A}\u{9}\u{1A}\u{4}\u{1B}\u{9}\u{1B}\u{4}\u{1C}\u{9}\u{1C}" . + "\u{4}\u{1D}\u{9}\u{1D}\u{4}\u{1E}\u{9}\u{1E}\u{3}\u{2}\u{3}\u{2}\u{3}" . "\u{3}\u{3}\u{3}\u{3}\u{4}\u{3}\u{4}\u{3}\u{4}\u{3}\u{5}\u{3}\u{5}" . "\u{3}\u{6}\u{3}\u{6}\u{3}\u{6}\u{3}\u{7}\u{3}\u{7}\u{3}\u{7}\u{3}" . - "\u{7}\u{5}\u{7}\u{42}\u{A}\u{7}\u{3}\u{8}\u{3}\u{8}\u{3}\u{8}\u{3}" . - "\u{8}\u{3}\u{8}\u{3}\u{8}\u{3}\u{8}\u{3}\u{8}\u{5}\u{8}\u{4C}\u{A}" . + "\u{7}\u{5}\u{7}\u{4E}\u{A}\u{7}\u{3}\u{8}\u{3}\u{8}\u{3}\u{8}\u{3}" . + "\u{8}\u{3}\u{8}\u{3}\u{8}\u{3}\u{8}\u{3}\u{8}\u{5}\u{8}\u{58}\u{A}" . "\u{8}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}" . - "\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{5}\u{9}\u{57}\u{A}\u{9}\u{3}\u{A}" . - "\u{3}\u{A}\u{3}\u{A}\u{3}\u{A}\u{6}\u{A}\u{5D}\u{A}\u{A}\u{D}\u{A}" . - "\u{E}\u{A}\u{5E}\u{3}\u{A}\u{3}\u{A}\u{3}\u{B}\u{3}\u{B}\u{3}\u{B}" . - "\u{3}\u{B}\u{3}\u{B}\u{3}\u{B}\u{5}\u{B}\u{69}\u{A}\u{B}\u{3}\u{C}" . - "\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{5}\u{C}\u{6F}\u{A}\u{C}\u{3}\u{D}" . - "\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}\u{5}\u{D}\u{77}" . - "\u{A}\u{D}\u{3}\u{E}\u{3}\u{E}\u{3}\u{E}\u{3}\u{E}\u{5}\u{E}\u{7D}" . - "\u{A}\u{E}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}" . - "\u{F}\u{3}\u{F}\u{3}\u{F}\u{5}\u{F}\u{87}\u{A}\u{F}\u{3}\u{10}\u{3}" . - "\u{10}\u{3}\u{11}\u{3}\u{11}\u{3}\u{12}\u{3}\u{12}\u{3}\u{13}\u{6}" . - "\u{13}\u{90}\u{A}\u{13}\u{D}\u{13}\u{E}\u{13}\u{91}\u{3}\u{14}\u{3}" . - "\u{14}\u{3}\u{14}\u{7}\u{14}\u{97}\u{A}\u{14}\u{C}\u{14}\u{E}\u{14}" . - "\u{9A}\u{B}\u{14}\u{3}\u{14}\u{5}\u{14}\u{9D}\u{A}\u{14}\u{3}\u{15}" . - "\u{3}\u{15}\u{3}\u{16}\u{3}\u{16}\u{3}\u{17}\u{3}\u{17}\u{3}\u{18}" . - "\u{6}\u{18}\u{A6}\u{A}\u{18}\u{D}\u{18}\u{E}\u{18}\u{A7}\u{3}\u{18}" . - "\u{3}\u{18}\u{2}\u{2}\u{19}\u{3}\u{3}\u{5}\u{4}\u{7}\u{5}\u{9}\u{6}" . - "\u{B}\u{7}\u{D}\u{8}\u{F}\u{9}\u{11}\u{A}\u{13}\u{B}\u{15}\u{C}\u{17}" . - "\u{D}\u{19}\u{E}\u{1B}\u{F}\u{1D}\u{10}\u{1F}\u{11}\u{21}\u{12}\u{23}" . - "\u{13}\u{25}\u{14}\u{27}\u{15}\u{29}\u{2}\u{2B}\u{2}\u{2D}\u{2}\u{2F}" . - "\u{16}\u{3}\u{2}\u{8}\u{3}\u{2}\u{29}\u{29}\u{4}\u{2}\u{30}\u{30}" . - "\u{61}\u{61}\u{3}\u{2}\u{63}\u{7C}\u{3}\u{2}\u{43}\u{5C}\u{3}\u{2}" . - "\u{32}\u{3B}\u{5}\u{2}\u{B}\u{C}\u{F}\u{F}\u{22}\u{22}\u{2}\u{B6}" . - "\u{2}\u{3}\u{3}\u{2}\u{2}\u{2}\u{2}\u{5}\u{3}\u{2}\u{2}\u{2}\u{2}" . - "\u{7}\u{3}\u{2}\u{2}\u{2}\u{2}\u{9}\u{3}\u{2}\u{2}\u{2}\u{2}\u{B}" . - "\u{3}\u{2}\u{2}\u{2}\u{2}\u{D}\u{3}\u{2}\u{2}\u{2}\u{2}\u{F}\u{3}" . - "\u{2}\u{2}\u{2}\u{2}\u{11}\u{3}\u{2}\u{2}\u{2}\u{2}\u{13}\u{3}\u{2}" . - "\u{2}\u{2}\u{2}\u{15}\u{3}\u{2}\u{2}\u{2}\u{2}\u{17}\u{3}\u{2}\u{2}" . - "\u{2}\u{2}\u{19}\u{3}\u{2}\u{2}\u{2}\u{2}\u{1B}\u{3}\u{2}\u{2}\u{2}" . - "\u{2}\u{1D}\u{3}\u{2}\u{2}\u{2}\u{2}\u{1F}\u{3}\u{2}\u{2}\u{2}\u{2}" . - "\u{21}\u{3}\u{2}\u{2}\u{2}\u{2}\u{23}\u{3}\u{2}\u{2}\u{2}\u{2}\u{25}" . - "\u{3}\u{2}\u{2}\u{2}\u{2}\u{27}\u{3}\u{2}\u{2}\u{2}\u{2}\u{2F}\u{3}" . - "\u{2}\u{2}\u{2}\u{3}\u{31}\u{3}\u{2}\u{2}\u{2}\u{5}\u{33}\u{3}\u{2}" . - "\u{2}\u{2}\u{7}\u{35}\u{3}\u{2}\u{2}\u{2}\u{9}\u{38}\u{3}\u{2}\u{2}" . - "\u{2}\u{B}\u{3A}\u{3}\u{2}\u{2}\u{2}\u{D}\u{41}\u{3}\u{2}\u{2}\u{2}" . - "\u{F}\u{4B}\u{3}\u{2}\u{2}\u{2}\u{11}\u{56}\u{3}\u{2}\u{2}\u{2}\u{13}" . - "\u{58}\u{3}\u{2}\u{2}\u{2}\u{15}\u{68}\u{3}\u{2}\u{2}\u{2}\u{17}\u{6E}" . - "\u{3}\u{2}\u{2}\u{2}\u{19}\u{76}\u{3}\u{2}\u{2}\u{2}\u{1B}\u{7C}\u{3}" . - "\u{2}\u{2}\u{2}\u{1D}\u{86}\u{3}\u{2}\u{2}\u{2}\u{1F}\u{88}\u{3}\u{2}" . - "\u{2}\u{2}\u{21}\u{8A}\u{3}\u{2}\u{2}\u{2}\u{23}\u{8C}\u{3}\u{2}\u{2}" . - "\u{2}\u{25}\u{8F}\u{3}\u{2}\u{2}\u{2}\u{27}\u{93}\u{3}\u{2}\u{2}\u{2}" . - "\u{29}\u{9E}\u{3}\u{2}\u{2}\u{2}\u{2B}\u{A0}\u{3}\u{2}\u{2}\u{2}\u{2D}" . - "\u{A2}\u{3}\u{2}\u{2}\u{2}\u{2F}\u{A5}\u{3}\u{2}\u{2}\u{2}\u{31}\u{32}" . - "\u{7}\u{3F}\u{2}\u{2}\u{32}\u{4}\u{3}\u{2}\u{2}\u{2}\u{33}\u{34}\u{7}" . - "\u{40}\u{2}\u{2}\u{34}\u{6}\u{3}\u{2}\u{2}\u{2}\u{35}\u{36}\u{7}\u{40}" . - "\u{2}\u{2}\u{36}\u{37}\u{7}\u{3F}\u{2}\u{2}\u{37}\u{8}\u{3}\u{2}\u{2}" . - "\u{2}\u{38}\u{39}\u{7}\u{3E}\u{2}\u{2}\u{39}\u{A}\u{3}\u{2}\u{2}\u{2}" . - "\u{3A}\u{3B}\u{7}\u{3E}\u{2}\u{2}\u{3B}\u{3C}\u{7}\u{3F}\u{2}\u{2}" . - "\u{3C}\u{C}\u{3}\u{2}\u{2}\u{2}\u{3D}\u{3E}\u{7}\u{3E}\u{2}\u{2}\u{3E}" . - "\u{42}\u{7}\u{40}\u{2}\u{2}\u{3F}\u{40}\u{7}\u{23}\u{2}\u{2}\u{40}" . - "\u{42}\u{7}\u{3F}\u{2}\u{2}\u{41}\u{3D}\u{3}\u{2}\u{2}\u{2}\u{41}" . - "\u{3F}\u{3}\u{2}\u{2}\u{2}\u{42}\u{E}\u{3}\u{2}\u{2}\u{2}\u{43}\u{44}" . - "\u{7}\u{6E}\u{2}\u{2}\u{44}\u{45}\u{7}\u{6B}\u{2}\u{2}\u{45}\u{46}" . - "\u{7}\u{6D}\u{2}\u{2}\u{46}\u{4C}\u{7}\u{67}\u{2}\u{2}\u{47}\u{48}" . - "\u{7}\u{4E}\u{2}\u{2}\u{48}\u{49}\u{7}\u{4B}\u{2}\u{2}\u{49}\u{4A}" . - "\u{7}\u{4D}\u{2}\u{2}\u{4A}\u{4C}\u{7}\u{47}\u{2}\u{2}\u{4B}\u{43}" . - "\u{3}\u{2}\u{2}\u{2}\u{4B}\u{47}\u{3}\u{2}\u{2}\u{2}\u{4C}\u{10}\u{3}" . - "\u{2}\u{2}\u{2}\u{4D}\u{4E}\u{7}\u{76}\u{2}\u{2}\u{4E}\u{4F}\u{7}" . - "\u{74}\u{2}\u{2}\u{4F}\u{50}\u{7}\u{77}\u{2}\u{2}\u{50}\u{57}\u{7}" . - "\u{67}\u{2}\u{2}\u{51}\u{52}\u{7}\u{68}\u{2}\u{2}\u{52}\u{53}\u{7}" . - "\u{63}\u{2}\u{2}\u{53}\u{54}\u{7}\u{6E}\u{2}\u{2}\u{54}\u{55}\u{7}" . - "\u{75}\u{2}\u{2}\u{55}\u{57}\u{7}\u{67}\u{2}\u{2}\u{56}\u{4D}\u{3}" . - "\u{2}\u{2}\u{2}\u{56}\u{51}\u{3}\u{2}\u{2}\u{2}\u{57}\u{12}\u{3}\u{2}" . - "\u{2}\u{2}\u{58}\u{5C}\u{7}\u{29}\u{2}\u{2}\u{59}\u{5D}\u{A}\u{2}" . - "\u{2}\u{2}\u{5A}\u{5B}\u{7}\u{29}\u{2}\u{2}\u{5B}\u{5D}\u{7}\u{29}" . - "\u{2}\u{2}\u{5C}\u{59}\u{3}\u{2}\u{2}\u{2}\u{5C}\u{5A}\u{3}\u{2}\u{2}" . - "\u{2}\u{5D}\u{5E}\u{3}\u{2}\u{2}\u{2}\u{5E}\u{5C}\u{3}\u{2}\u{2}\u{2}" . - "\u{5E}\u{5F}\u{3}\u{2}\u{2}\u{2}\u{5F}\u{60}\u{3}\u{2}\u{2}\u{2}\u{60}" . - "\u{61}\u{7}\u{29}\u{2}\u{2}\u{61}\u{14}\u{3}\u{2}\u{2}\u{2}\u{62}" . - "\u{63}\u{7}\u{63}\u{2}\u{2}\u{63}\u{64}\u{7}\u{70}\u{2}\u{2}\u{64}" . - "\u{69}\u{7}\u{66}\u{2}\u{2}\u{65}\u{66}\u{7}\u{43}\u{2}\u{2}\u{66}" . - "\u{67}\u{7}\u{50}\u{2}\u{2}\u{67}\u{69}\u{7}\u{46}\u{2}\u{2}\u{68}" . - "\u{62}\u{3}\u{2}\u{2}\u{2}\u{68}\u{65}\u{3}\u{2}\u{2}\u{2}\u{69}\u{16}" . - "\u{3}\u{2}\u{2}\u{2}\u{6A}\u{6B}\u{7}\u{71}\u{2}\u{2}\u{6B}\u{6F}" . - "\u{7}\u{74}\u{2}\u{2}\u{6C}\u{6D}\u{7}\u{51}\u{2}\u{2}\u{6D}\u{6F}" . - "\u{7}\u{54}\u{2}\u{2}\u{6E}\u{6A}\u{3}\u{2}\u{2}\u{2}\u{6E}\u{6C}" . - "\u{3}\u{2}\u{2}\u{2}\u{6F}\u{18}\u{3}\u{2}\u{2}\u{2}\u{70}\u{71}\u{7}" . - "\u{70}\u{2}\u{2}\u{71}\u{72}\u{7}\u{71}\u{2}\u{2}\u{72}\u{77}\u{7}" . - "\u{76}\u{2}\u{2}\u{73}\u{74}\u{7}\u{50}\u{2}\u{2}\u{74}\u{75}\u{7}" . - "\u{51}\u{2}\u{2}\u{75}\u{77}\u{7}\u{56}\u{2}\u{2}\u{76}\u{70}\u{3}" . - "\u{2}\u{2}\u{2}\u{76}\u{73}\u{3}\u{2}\u{2}\u{2}\u{77}\u{1A}\u{3}\u{2}" . - "\u{2}\u{2}\u{78}\u{79}\u{7}\u{6B}\u{2}\u{2}\u{79}\u{7D}\u{7}\u{75}" . - "\u{2}\u{2}\u{7A}\u{7B}\u{7}\u{4B}\u{2}\u{2}\u{7B}\u{7D}\u{7}\u{55}" . - "\u{2}\u{2}\u{7C}\u{78}\u{3}\u{2}\u{2}\u{2}\u{7C}\u{7A}\u{3}\u{2}\u{2}" . - "\u{2}\u{7D}\u{1C}\u{3}\u{2}\u{2}\u{2}\u{7E}\u{7F}\u{7}\u{70}\u{2}" . - "\u{2}\u{7F}\u{80}\u{7}\u{77}\u{2}\u{2}\u{80}\u{81}\u{7}\u{6E}\u{2}" . - "\u{2}\u{81}\u{87}\u{7}\u{6E}\u{2}\u{2}\u{82}\u{83}\u{7}\u{50}\u{2}" . - "\u{2}\u{83}\u{84}\u{7}\u{57}\u{2}\u{2}\u{84}\u{85}\u{7}\u{4E}\u{2}" . - "\u{2}\u{85}\u{87}\u{7}\u{4E}\u{2}\u{2}\u{86}\u{7E}\u{3}\u{2}\u{2}" . - "\u{2}\u{86}\u{82}\u{3}\u{2}\u{2}\u{2}\u{87}\u{1E}\u{3}\u{2}\u{2}\u{2}" . - "\u{88}\u{89}\u{7}\u{2A}\u{2}\u{2}\u{89}\u{20}\u{3}\u{2}\u{2}\u{2}" . - "\u{8A}\u{8B}\u{7}\u{2B}\u{2}\u{2}\u{8B}\u{22}\u{3}\u{2}\u{2}\u{2}" . - "\u{8C}\u{8D}\u{7}\u{30}\u{2}\u{2}\u{8D}\u{24}\u{3}\u{2}\u{2}\u{2}" . - "\u{8E}\u{90}\u{5}\u{2D}\u{17}\u{2}\u{8F}\u{8E}\u{3}\u{2}\u{2}\u{2}" . - "\u{90}\u{91}\u{3}\u{2}\u{2}\u{2}\u{91}\u{8F}\u{3}\u{2}\u{2}\u{2}\u{91}" . - "\u{92}\u{3}\u{2}\u{2}\u{2}\u{92}\u{26}\u{3}\u{2}\u{2}\u{2}\u{93}\u{9C}" . - "\u{5}\u{29}\u{15}\u{2}\u{94}\u{97}\u{5}\u{29}\u{15}\u{2}\u{95}\u{97}" . - "\u{9}\u{3}\u{2}\u{2}\u{96}\u{94}\u{3}\u{2}\u{2}\u{2}\u{96}\u{95}\u{3}" . - "\u{2}\u{2}\u{2}\u{97}\u{9A}\u{3}\u{2}\u{2}\u{2}\u{98}\u{96}\u{3}\u{2}" . - "\u{2}\u{2}\u{98}\u{99}\u{3}\u{2}\u{2}\u{2}\u{99}\u{9B}\u{3}\u{2}\u{2}" . - "\u{2}\u{9A}\u{98}\u{3}\u{2}\u{2}\u{2}\u{9B}\u{9D}\u{5}\u{29}\u{15}" . - "\u{2}\u{9C}\u{98}\u{3}\u{2}\u{2}\u{2}\u{9C}\u{9D}\u{3}\u{2}\u{2}\u{2}" . - "\u{9D}\u{28}\u{3}\u{2}\u{2}\u{2}\u{9E}\u{9F}\u{9}\u{4}\u{2}\u{2}\u{9F}" . - "\u{2A}\u{3}\u{2}\u{2}\u{2}\u{A0}\u{A1}\u{9}\u{5}\u{2}\u{2}\u{A1}\u{2C}" . - "\u{3}\u{2}\u{2}\u{2}\u{A2}\u{A3}\u{9}\u{6}\u{2}\u{2}\u{A3}\u{2E}\u{3}" . - "\u{2}\u{2}\u{2}\u{A4}\u{A6}\u{9}\u{7}\u{2}\u{2}\u{A5}\u{A4}\u{3}\u{2}" . - "\u{2}\u{2}\u{A6}\u{A7}\u{3}\u{2}\u{2}\u{2}\u{A7}\u{A5}\u{3}\u{2}\u{2}" . - "\u{2}\u{A7}\u{A8}\u{3}\u{2}\u{2}\u{2}\u{A8}\u{A9}\u{3}\u{2}\u{2}\u{2}" . - "\u{A9}\u{AA}\u{8}\u{18}\u{2}\u{2}\u{AA}\u{30}\u{3}\u{2}\u{2}\u{2}" . - "\u{12}\u{2}\u{41}\u{4B}\u{56}\u{5C}\u{5E}\u{68}\u{6E}\u{76}\u{7C}" . - "\u{86}\u{91}\u{96}\u{98}\u{9C}\u{A7}\u{3}\u{8}\u{2}\u{2}"; + "\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}" . + "\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{5}\u{9}\u{6A}\u{A}\u{9}\u{3}" . + "\u{A}\u{3}\u{A}\u{7}\u{A}\u{6E}\u{A}\u{A}\u{C}\u{A}\u{E}\u{A}\u{71}" . + "\u{B}\u{A}\u{3}\u{A}\u{3}\u{A}\u{3}\u{B}\u{3}\u{B}\u{7}\u{B}\u{77}" . + "\u{A}\u{B}\u{C}\u{B}\u{E}\u{B}\u{7A}\u{B}\u{B}\u{3}\u{B}\u{3}\u{B}" . + "\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}" . + "\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{3}\u{C}\u{5}\u{C}" . + "\u{8A}\u{A}\u{C}\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}" . + "\u{3}\u{D}\u{3}\u{D}\u{3}\u{D}\u{5}\u{D}\u{94}\u{A}\u{D}\u{3}\u{E}" . + "\u{3}\u{E}\u{3}\u{E}\u{3}\u{E}\u{3}\u{E}\u{3}\u{E}\u{3}\u{E}\u{3}" . + "\u{E}\u{5}\u{E}\u{9E}\u{A}\u{E}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}" . + "\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{3}\u{F}\u{5}\u{F}" . + "\u{A9}\u{A}\u{F}\u{3}\u{10}\u{3}\u{10}\u{3}\u{10}\u{3}\u{10}\u{6}" . + "\u{10}\u{AF}\u{A}\u{10}\u{D}\u{10}\u{E}\u{10}\u{B0}\u{3}\u{10}\u{3}" . + "\u{10}\u{3}\u{11}\u{3}\u{11}\u{3}\u{11}\u{3}\u{11}\u{3}\u{11}\u{3}" . + "\u{11}\u{5}\u{11}\u{BB}\u{A}\u{11}\u{3}\u{12}\u{3}\u{12}\u{3}\u{12}" . + "\u{3}\u{12}\u{5}\u{12}\u{C1}\u{A}\u{12}\u{3}\u{13}\u{3}\u{13}\u{3}" . + "\u{13}\u{3}\u{13}\u{5}\u{13}\u{C7}\u{A}\u{13}\u{3}\u{14}\u{3}\u{14}" . + "\u{3}\u{14}\u{3}\u{14}\u{3}\u{14}\u{3}\u{14}\u{3}\u{14}\u{3}\u{14}" . + "\u{5}\u{14}\u{D1}\u{A}\u{14}\u{3}\u{15}\u{3}\u{15}\u{3}\u{15}\u{3}" . + "\u{15}\u{3}\u{15}\u{3}\u{15}\u{5}\u{15}\u{D9}\u{A}\u{15}\u{3}\u{16}" . + "\u{3}\u{16}\u{3}\u{17}\u{3}\u{17}\u{3}\u{18}\u{6}\u{18}\u{E0}\u{A}" . + "\u{18}\u{D}\u{18}\u{E}\u{18}\u{E1}\u{3}\u{19}\u{3}\u{19}\u{3}\u{1A}" . + "\u{3}\u{1A}\u{3}\u{1A}\u{3}\u{1A}\u{7}\u{1A}\u{EA}\u{A}\u{1A}\u{C}" . + "\u{1A}\u{E}\u{1A}\u{ED}\u{B}\u{1A}\u{3}\u{1A}\u{5}\u{1A}\u{F0}\u{A}" . + "\u{1A}\u{3}\u{1B}\u{3}\u{1B}\u{3}\u{1C}\u{3}\u{1C}\u{3}\u{1D}\u{3}" . + "\u{1D}\u{3}\u{1E}\u{6}\u{1E}\u{F9}\u{A}\u{1E}\u{D}\u{1E}\u{E}\u{1E}" . + "\u{FA}\u{3}\u{1E}\u{3}\u{1E}\u{2}\u{2}\u{1F}\u{3}\u{3}\u{5}\u{4}\u{7}" . + "\u{5}\u{9}\u{6}\u{B}\u{7}\u{D}\u{8}\u{F}\u{9}\u{11}\u{A}\u{13}\u{B}" . + "\u{15}\u{C}\u{17}\u{D}\u{19}\u{E}\u{1B}\u{F}\u{1D}\u{10}\u{1F}\u{11}" . + "\u{21}\u{12}\u{23}\u{13}\u{25}\u{14}\u{27}\u{15}\u{29}\u{16}\u{2B}" . + "\u{17}\u{2D}\u{18}\u{2F}\u{19}\u{31}\u{1A}\u{33}\u{1B}\u{35}\u{2}" . + "\u{37}\u{2}\u{39}\u{2}\u{3B}\u{1C}\u{3}\u{2}\u{7}\u{3}\u{2}\u{29}" . + "\u{29}\u{3}\u{2}\u{63}\u{7C}\u{3}\u{2}\u{43}\u{5C}\u{3}\u{2}\u{32}" . + "\u{3B}\u{5}\u{2}\u{B}\u{C}\u{F}\u{F}\u{22}\u{22}\u{2}\u{110}\u{2}" . + "\u{3}\u{3}\u{2}\u{2}\u{2}\u{2}\u{5}\u{3}\u{2}\u{2}\u{2}\u{2}\u{7}" . + "\u{3}\u{2}\u{2}\u{2}\u{2}\u{9}\u{3}\u{2}\u{2}\u{2}\u{2}\u{B}\u{3}" . + "\u{2}\u{2}\u{2}\u{2}\u{D}\u{3}\u{2}\u{2}\u{2}\u{2}\u{F}\u{3}\u{2}" . + "\u{2}\u{2}\u{2}\u{11}\u{3}\u{2}\u{2}\u{2}\u{2}\u{13}\u{3}\u{2}\u{2}" . + "\u{2}\u{2}\u{15}\u{3}\u{2}\u{2}\u{2}\u{2}\u{17}\u{3}\u{2}\u{2}\u{2}" . + "\u{2}\u{19}\u{3}\u{2}\u{2}\u{2}\u{2}\u{1B}\u{3}\u{2}\u{2}\u{2}\u{2}" . + "\u{1D}\u{3}\u{2}\u{2}\u{2}\u{2}\u{1F}\u{3}\u{2}\u{2}\u{2}\u{2}\u{21}" . + "\u{3}\u{2}\u{2}\u{2}\u{2}\u{23}\u{3}\u{2}\u{2}\u{2}\u{2}\u{25}\u{3}" . + "\u{2}\u{2}\u{2}\u{2}\u{27}\u{3}\u{2}\u{2}\u{2}\u{2}\u{29}\u{3}\u{2}" . + "\u{2}\u{2}\u{2}\u{2B}\u{3}\u{2}\u{2}\u{2}\u{2}\u{2D}\u{3}\u{2}\u{2}" . + "\u{2}\u{2}\u{2F}\u{3}\u{2}\u{2}\u{2}\u{2}\u{31}\u{3}\u{2}\u{2}\u{2}" . + "\u{2}\u{33}\u{3}\u{2}\u{2}\u{2}\u{2}\u{3B}\u{3}\u{2}\u{2}\u{2}\u{3}" . + "\u{3D}\u{3}\u{2}\u{2}\u{2}\u{5}\u{3F}\u{3}\u{2}\u{2}\u{2}\u{7}\u{41}" . + "\u{3}\u{2}\u{2}\u{2}\u{9}\u{44}\u{3}\u{2}\u{2}\u{2}\u{B}\u{46}\u{3}" . + "\u{2}\u{2}\u{2}\u{D}\u{4D}\u{3}\u{2}\u{2}\u{2}\u{F}\u{57}\u{3}\u{2}" . + "\u{2}\u{2}\u{11}\u{69}\u{3}\u{2}\u{2}\u{2}\u{13}\u{6B}\u{3}\u{2}\u{2}" . + "\u{2}\u{15}\u{74}\u{3}\u{2}\u{2}\u{2}\u{17}\u{89}\u{3}\u{2}\u{2}\u{2}" . + "\u{19}\u{93}\u{3}\u{2}\u{2}\u{2}\u{1B}\u{9D}\u{3}\u{2}\u{2}\u{2}\u{1D}" . + "\u{A8}\u{3}\u{2}\u{2}\u{2}\u{1F}\u{AA}\u{3}\u{2}\u{2}\u{2}\u{21}\u{BA}" . + "\u{3}\u{2}\u{2}\u{2}\u{23}\u{C0}\u{3}\u{2}\u{2}\u{2}\u{25}\u{C6}\u{3}" . + "\u{2}\u{2}\u{2}\u{27}\u{D0}\u{3}\u{2}\u{2}\u{2}\u{29}\u{D8}\u{3}\u{2}" . + "\u{2}\u{2}\u{2B}\u{DA}\u{3}\u{2}\u{2}\u{2}\u{2D}\u{DC}\u{3}\u{2}\u{2}" . + "\u{2}\u{2F}\u{DF}\u{3}\u{2}\u{2}\u{2}\u{31}\u{E3}\u{3}\u{2}\u{2}\u{2}" . + "\u{33}\u{E5}\u{3}\u{2}\u{2}\u{2}\u{35}\u{F1}\u{3}\u{2}\u{2}\u{2}\u{37}" . + "\u{F3}\u{3}\u{2}\u{2}\u{2}\u{39}\u{F5}\u{3}\u{2}\u{2}\u{2}\u{3B}\u{F8}" . + "\u{3}\u{2}\u{2}\u{2}\u{3D}\u{3E}\u{7}\u{3F}\u{2}\u{2}\u{3E}\u{4}\u{3}" . + "\u{2}\u{2}\u{2}\u{3F}\u{40}\u{7}\u{40}\u{2}\u{2}\u{40}\u{6}\u{3}\u{2}" . + "\u{2}\u{2}\u{41}\u{42}\u{7}\u{40}\u{2}\u{2}\u{42}\u{43}\u{7}\u{3F}" . + "\u{2}\u{2}\u{43}\u{8}\u{3}\u{2}\u{2}\u{2}\u{44}\u{45}\u{7}\u{3E}\u{2}" . + "\u{2}\u{45}\u{A}\u{3}\u{2}\u{2}\u{2}\u{46}\u{47}\u{7}\u{3E}\u{2}\u{2}" . + "\u{47}\u{48}\u{7}\u{3F}\u{2}\u{2}\u{48}\u{C}\u{3}\u{2}\u{2}\u{2}\u{49}" . + "\u{4A}\u{7}\u{3E}\u{2}\u{2}\u{4A}\u{4E}\u{7}\u{40}\u{2}\u{2}\u{4B}" . + "\u{4C}\u{7}\u{23}\u{2}\u{2}\u{4C}\u{4E}\u{7}\u{3F}\u{2}\u{2}\u{4D}" . + "\u{49}\u{3}\u{2}\u{2}\u{2}\u{4D}\u{4B}\u{3}\u{2}\u{2}\u{2}\u{4E}\u{E}" . + "\u{3}\u{2}\u{2}\u{2}\u{4F}\u{50}\u{7}\u{6E}\u{2}\u{2}\u{50}\u{51}" . + "\u{7}\u{6B}\u{2}\u{2}\u{51}\u{52}\u{7}\u{6D}\u{2}\u{2}\u{52}\u{58}" . + "\u{7}\u{67}\u{2}\u{2}\u{53}\u{54}\u{7}\u{4E}\u{2}\u{2}\u{54}\u{55}" . + "\u{7}\u{4B}\u{2}\u{2}\u{55}\u{56}\u{7}\u{4D}\u{2}\u{2}\u{56}\u{58}" . + "\u{7}\u{47}\u{2}\u{2}\u{57}\u{4F}\u{3}\u{2}\u{2}\u{2}\u{57}\u{53}" . + "\u{3}\u{2}\u{2}\u{2}\u{58}\u{10}\u{3}\u{2}\u{2}\u{2}\u{59}\u{5A}\u{7}" . + "\u{65}\u{2}\u{2}\u{5A}\u{5B}\u{7}\u{71}\u{2}\u{2}\u{5B}\u{5C}\u{7}" . + "\u{70}\u{2}\u{2}\u{5C}\u{5D}\u{7}\u{76}\u{2}\u{2}\u{5D}\u{5E}\u{7}" . + "\u{63}\u{2}\u{2}\u{5E}\u{5F}\u{7}\u{6B}\u{2}\u{2}\u{5F}\u{60}\u{7}" . + "\u{70}\u{2}\u{2}\u{60}\u{6A}\u{7}\u{75}\u{2}\u{2}\u{61}\u{62}\u{7}" . + "\u{45}\u{2}\u{2}\u{62}\u{63}\u{7}\u{51}\u{2}\u{2}\u{63}\u{64}\u{7}" . + "\u{50}\u{2}\u{2}\u{64}\u{65}\u{7}\u{56}\u{2}\u{2}\u{65}\u{66}\u{7}" . + "\u{43}\u{2}\u{2}\u{66}\u{67}\u{7}\u{4B}\u{2}\u{2}\u{67}\u{68}\u{7}" . + "\u{50}\u{2}\u{2}\u{68}\u{6A}\u{7}\u{55}\u{2}\u{2}\u{69}\u{59}\u{3}" . + "\u{2}\u{2}\u{2}\u{69}\u{61}\u{3}\u{2}\u{2}\u{2}\u{6A}\u{12}\u{3}\u{2}" . + "\u{2}\u{2}\u{6B}\u{6F}\u{5}\u{17}\u{C}\u{2}\u{6C}\u{6E}\u{7}\u{22}" . + "\u{2}\u{2}\u{6D}\u{6C}\u{3}\u{2}\u{2}\u{2}\u{6E}\u{71}\u{3}\u{2}\u{2}" . + "\u{2}\u{6F}\u{6D}\u{3}\u{2}\u{2}\u{2}\u{6F}\u{70}\u{3}\u{2}\u{2}\u{2}" . + "\u{70}\u{72}\u{3}\u{2}\u{2}\u{2}\u{71}\u{6F}\u{3}\u{2}\u{2}\u{2}\u{72}" . + "\u{73}\u{5}\u{1B}\u{E}\u{2}\u{73}\u{14}\u{3}\u{2}\u{2}\u{2}\u{74}" . + "\u{78}\u{5}\u{19}\u{D}\u{2}\u{75}\u{77}\u{7}\u{22}\u{2}\u{2}\u{76}" . + "\u{75}\u{3}\u{2}\u{2}\u{2}\u{77}\u{7A}\u{3}\u{2}\u{2}\u{2}\u{78}\u{76}" . + "\u{3}\u{2}\u{2}\u{2}\u{78}\u{79}\u{3}\u{2}\u{2}\u{2}\u{79}\u{7B}\u{3}" . + "\u{2}\u{2}\u{2}\u{7A}\u{78}\u{3}\u{2}\u{2}\u{2}\u{7B}\u{7C}\u{5}\u{1B}" . + "\u{E}\u{2}\u{7C}\u{16}\u{3}\u{2}\u{2}\u{2}\u{7D}\u{7E}\u{7}\u{75}" . + "\u{2}\u{2}\u{7E}\u{7F}\u{7}\u{76}\u{2}\u{2}\u{7F}\u{80}\u{7}\u{63}" . + "\u{2}\u{2}\u{80}\u{81}\u{7}\u{74}\u{2}\u{2}\u{81}\u{82}\u{7}\u{76}" . + "\u{2}\u{2}\u{82}\u{8A}\u{7}\u{75}\u{2}\u{2}\u{83}\u{84}\u{7}\u{55}" . + "\u{2}\u{2}\u{84}\u{85}\u{7}\u{56}\u{2}\u{2}\u{85}\u{86}\u{7}\u{43}" . + "\u{2}\u{2}\u{86}\u{87}\u{7}\u{54}\u{2}\u{2}\u{87}\u{88}\u{7}\u{56}" . + "\u{2}\u{2}\u{88}\u{8A}\u{7}\u{55}\u{2}\u{2}\u{89}\u{7D}\u{3}\u{2}" . + "\u{2}\u{2}\u{89}\u{83}\u{3}\u{2}\u{2}\u{2}\u{8A}\u{18}\u{3}\u{2}\u{2}" . + "\u{2}\u{8B}\u{8C}\u{7}\u{67}\u{2}\u{2}\u{8C}\u{8D}\u{7}\u{70}\u{2}" . + "\u{2}\u{8D}\u{8E}\u{7}\u{66}\u{2}\u{2}\u{8E}\u{94}\u{7}\u{75}\u{2}" . + "\u{2}\u{8F}\u{90}\u{7}\u{47}\u{2}\u{2}\u{90}\u{91}\u{7}\u{50}\u{2}" . + "\u{2}\u{91}\u{92}\u{7}\u{46}\u{2}\u{2}\u{92}\u{94}\u{7}\u{55}\u{2}" . + "\u{2}\u{93}\u{8B}\u{3}\u{2}\u{2}\u{2}\u{93}\u{8F}\u{3}\u{2}\u{2}\u{2}" . + "\u{94}\u{1A}\u{3}\u{2}\u{2}\u{2}\u{95}\u{96}\u{7}\u{79}\u{2}\u{2}" . + "\u{96}\u{97}\u{7}\u{6B}\u{2}\u{2}\u{97}\u{98}\u{7}\u{76}\u{2}\u{2}" . + "\u{98}\u{9E}\u{7}\u{6A}\u{2}\u{2}\u{99}\u{9A}\u{7}\u{59}\u{2}\u{2}" . + "\u{9A}\u{9B}\u{7}\u{4B}\u{2}\u{2}\u{9B}\u{9C}\u{7}\u{56}\u{2}\u{2}" . + "\u{9C}\u{9E}\u{7}\u{4A}\u{2}\u{2}\u{9D}\u{95}\u{3}\u{2}\u{2}\u{2}" . + "\u{9D}\u{99}\u{3}\u{2}\u{2}\u{2}\u{9E}\u{1C}\u{3}\u{2}\u{2}\u{2}\u{9F}" . + "\u{A0}\u{7}\u{76}\u{2}\u{2}\u{A0}\u{A1}\u{7}\u{74}\u{2}\u{2}\u{A1}" . + "\u{A2}\u{7}\u{77}\u{2}\u{2}\u{A2}\u{A9}\u{7}\u{67}\u{2}\u{2}\u{A3}" . + "\u{A4}\u{7}\u{68}\u{2}\u{2}\u{A4}\u{A5}\u{7}\u{63}\u{2}\u{2}\u{A5}" . + "\u{A6}\u{7}\u{6E}\u{2}\u{2}\u{A6}\u{A7}\u{7}\u{75}\u{2}\u{2}\u{A7}" . + "\u{A9}\u{7}\u{67}\u{2}\u{2}\u{A8}\u{9F}\u{3}\u{2}\u{2}\u{2}\u{A8}" . + "\u{A3}\u{3}\u{2}\u{2}\u{2}\u{A9}\u{1E}\u{3}\u{2}\u{2}\u{2}\u{AA}\u{AE}" . + "\u{7}\u{29}\u{2}\u{2}\u{AB}\u{AF}\u{A}\u{2}\u{2}\u{2}\u{AC}\u{AD}" . + "\u{7}\u{29}\u{2}\u{2}\u{AD}\u{AF}\u{7}\u{29}\u{2}\u{2}\u{AE}\u{AB}" . + "\u{3}\u{2}\u{2}\u{2}\u{AE}\u{AC}\u{3}\u{2}\u{2}\u{2}\u{AF}\u{B0}\u{3}" . + "\u{2}\u{2}\u{2}\u{B0}\u{AE}\u{3}\u{2}\u{2}\u{2}\u{B0}\u{B1}\u{3}\u{2}" . + "\u{2}\u{2}\u{B1}\u{B2}\u{3}\u{2}\u{2}\u{2}\u{B2}\u{B3}\u{7}\u{29}" . + "\u{2}\u{2}\u{B3}\u{20}\u{3}\u{2}\u{2}\u{2}\u{B4}\u{B5}\u{7}\u{63}" . + "\u{2}\u{2}\u{B5}\u{B6}\u{7}\u{70}\u{2}\u{2}\u{B6}\u{BB}\u{7}\u{66}" . + "\u{2}\u{2}\u{B7}\u{B8}\u{7}\u{43}\u{2}\u{2}\u{B8}\u{B9}\u{7}\u{50}" . + "\u{2}\u{2}\u{B9}\u{BB}\u{7}\u{46}\u{2}\u{2}\u{BA}\u{B4}\u{3}\u{2}" . + "\u{2}\u{2}\u{BA}\u{B7}\u{3}\u{2}\u{2}\u{2}\u{BB}\u{22}\u{3}\u{2}\u{2}" . + "\u{2}\u{BC}\u{BD}\u{7}\u{71}\u{2}\u{2}\u{BD}\u{C1}\u{7}\u{74}\u{2}" . + "\u{2}\u{BE}\u{BF}\u{7}\u{51}\u{2}\u{2}\u{BF}\u{C1}\u{7}\u{54}\u{2}" . + "\u{2}\u{C0}\u{BC}\u{3}\u{2}\u{2}\u{2}\u{C0}\u{BE}\u{3}\u{2}\u{2}\u{2}" . + "\u{C1}\u{24}\u{3}\u{2}\u{2}\u{2}\u{C2}\u{C3}\u{7}\u{6B}\u{2}\u{2}" . + "\u{C3}\u{C7}\u{7}\u{75}\u{2}\u{2}\u{C4}\u{C5}\u{7}\u{4B}\u{2}\u{2}" . + "\u{C5}\u{C7}\u{7}\u{55}\u{2}\u{2}\u{C6}\u{C2}\u{3}\u{2}\u{2}\u{2}" . + "\u{C6}\u{C4}\u{3}\u{2}\u{2}\u{2}\u{C7}\u{26}\u{3}\u{2}\u{2}\u{2}\u{C8}" . + "\u{C9}\u{7}\u{70}\u{2}\u{2}\u{C9}\u{CA}\u{7}\u{77}\u{2}\u{2}\u{CA}" . + "\u{CB}\u{7}\u{6E}\u{2}\u{2}\u{CB}\u{D1}\u{7}\u{6E}\u{2}\u{2}\u{CC}" . + "\u{CD}\u{7}\u{50}\u{2}\u{2}\u{CD}\u{CE}\u{7}\u{57}\u{2}\u{2}\u{CE}" . + "\u{CF}\u{7}\u{4E}\u{2}\u{2}\u{CF}\u{D1}\u{7}\u{4E}\u{2}\u{2}\u{D0}" . + "\u{C8}\u{3}\u{2}\u{2}\u{2}\u{D0}\u{CC}\u{3}\u{2}\u{2}\u{2}\u{D1}\u{28}" . + "\u{3}\u{2}\u{2}\u{2}\u{D2}\u{D3}\u{7}\u{70}\u{2}\u{2}\u{D3}\u{D4}" . + "\u{7}\u{71}\u{2}\u{2}\u{D4}\u{D9}\u{7}\u{76}\u{2}\u{2}\u{D5}\u{D6}" . + "\u{7}\u{50}\u{2}\u{2}\u{D6}\u{D7}\u{7}\u{51}\u{2}\u{2}\u{D7}\u{D9}" . + "\u{7}\u{56}\u{2}\u{2}\u{D8}\u{D2}\u{3}\u{2}\u{2}\u{2}\u{D8}\u{D5}" . + "\u{3}\u{2}\u{2}\u{2}\u{D9}\u{2A}\u{3}\u{2}\u{2}\u{2}\u{DA}\u{DB}\u{7}" . + "\u{2A}\u{2}\u{2}\u{DB}\u{2C}\u{3}\u{2}\u{2}\u{2}\u{DC}\u{DD}\u{7}" . + "\u{2B}\u{2}\u{2}\u{DD}\u{2E}\u{3}\u{2}\u{2}\u{2}\u{DE}\u{E0}\u{5}" . + "\u{39}\u{1D}\u{2}\u{DF}\u{DE}\u{3}\u{2}\u{2}\u{2}\u{E0}\u{E1}\u{3}" . + "\u{2}\u{2}\u{2}\u{E1}\u{DF}\u{3}\u{2}\u{2}\u{2}\u{E1}\u{E2}\u{3}\u{2}" . + "\u{2}\u{2}\u{E2}\u{30}\u{3}\u{2}\u{2}\u{2}\u{E3}\u{E4}\u{7}\u{30}" . + "\u{2}\u{2}\u{E4}\u{32}\u{3}\u{2}\u{2}\u{2}\u{E5}\u{EF}\u{5}\u{35}" . + "\u{1B}\u{2}\u{E6}\u{EA}\u{5}\u{35}\u{1B}\u{2}\u{E7}\u{EA}\u{7}\u{61}" . + "\u{2}\u{2}\u{E8}\u{EA}\u{5}\u{31}\u{19}\u{2}\u{E9}\u{E6}\u{3}\u{2}" . + "\u{2}\u{2}\u{E9}\u{E7}\u{3}\u{2}\u{2}\u{2}\u{E9}\u{E8}\u{3}\u{2}\u{2}" . + "\u{2}\u{EA}\u{ED}\u{3}\u{2}\u{2}\u{2}\u{EB}\u{E9}\u{3}\u{2}\u{2}\u{2}" . + "\u{EB}\u{EC}\u{3}\u{2}\u{2}\u{2}\u{EC}\u{EE}\u{3}\u{2}\u{2}\u{2}\u{ED}" . + "\u{EB}\u{3}\u{2}\u{2}\u{2}\u{EE}\u{F0}\u{5}\u{35}\u{1B}\u{2}\u{EF}" . + "\u{EB}\u{3}\u{2}\u{2}\u{2}\u{EF}\u{F0}\u{3}\u{2}\u{2}\u{2}\u{F0}\u{34}" . + "\u{3}\u{2}\u{2}\u{2}\u{F1}\u{F2}\u{9}\u{3}\u{2}\u{2}\u{F2}\u{36}\u{3}" . + "\u{2}\u{2}\u{2}\u{F3}\u{F4}\u{9}\u{4}\u{2}\u{2}\u{F4}\u{38}\u{3}\u{2}" . + "\u{2}\u{2}\u{F5}\u{F6}\u{9}\u{5}\u{2}\u{2}\u{F6}\u{3A}\u{3}\u{2}\u{2}" . + "\u{2}\u{F7}\u{F9}\u{9}\u{6}\u{2}\u{2}\u{F8}\u{F7}\u{3}\u{2}\u{2}\u{2}" . + "\u{F9}\u{FA}\u{3}\u{2}\u{2}\u{2}\u{FA}\u{F8}\u{3}\u{2}\u{2}\u{2}\u{FA}" . + "\u{FB}\u{3}\u{2}\u{2}\u{2}\u{FB}\u{FC}\u{3}\u{2}\u{2}\u{2}\u{FC}\u{FD}" . + "\u{8}\u{1E}\u{2}\u{2}\u{FD}\u{3C}\u{3}\u{2}\u{2}\u{2}\u{18}\u{2}\u{4D}" . + "\u{57}\u{69}\u{6F}\u{78}\u{89}\u{93}\u{9D}\u{A8}\u{AE}\u{B0}\u{BA}" . + "\u{C0}\u{C6}\u{D0}\u{D8}\u{E1}\u{E9}\u{EB}\u{EF}\u{FA}\u{3}\u{8}\u{2}" . + "\u{2}"; protected static $atn; protected static $decisionToDFA; diff --git a/src/ApiFilter/Parser/ApiFilterLexer.tokens b/src/ApiFilter/Parser/ApiFilterLexer.tokens index 5d0034b..eb9e16e 100644 --- a/src/ApiFilter/Parser/ApiFilterLexer.tokens +++ b/src/ApiFilter/Parser/ApiFilterLexer.tokens @@ -5,24 +5,30 @@ LT=4 LTE=5 NEQ=6 LIKE=7 -BOOL=8 -STRING=9 -AND=10 -OR=11 -NOT=12 -IS=13 -NULL=14 -OPEN_PAR=15 -CLOSE_PAR=16 -DOT=17 -INT=18 -FIELD=19 -WS=20 +CONTAINS=8 +STARTSWITH=9 +ENDSWITH=10 +STARTS=11 +ENDS=12 +WITH=13 +BOOL=14 +STRING=15 +AND=16 +OR=17 +IS=18 +NULL=19 +NOT=20 +OPEN_PAR=21 +CLOSE_PAR=22 +INT=23 +DOT=24 +FIELD=25 +WS=26 '='=1 '>'=2 '>='=3 '<'=4 '<='=5 -'('=15 -')'=16 -'.'=17 +'('=21 +')'=22 +'.'=24 diff --git a/src/ApiFilter/Parser/ApiFilterParser.php b/src/ApiFilter/Parser/ApiFilterParser.php index b2bf30b..d285ebd 100644 --- a/src/ApiFilter/Parser/ApiFilterParser.php +++ b/src/ApiFilter/Parser/ApiFilterParser.php @@ -24,20 +24,22 @@ final class ApiFilterParser extends Parser { public const EQ = 1, GT = 2, GTE = 3, LT = 4, LTE = 5, NEQ = 6, LIKE = 7, - BOOL = 8, STRING = 9, AND = 10, OR = 11, NOT = 12, IS = 13, - NULL = 14, OPEN_PAR = 15, CLOSE_PAR = 16, DOT = 17, INT = 18, - FIELD = 19, WS = 20; + CONTAINS = 8, STARTSWITH = 9, ENDSWITH = 10, STARTS = 11, + ENDS = 12, WITH = 13, BOOL = 14, STRING = 15, AND = 16, OR = 17, + IS = 18, NULL = 19, NOT = 20, OPEN_PAR = 21, CLOSE_PAR = 22, + INT = 23, DOT = 24, FIELD = 25, WS = 26; public const RULE_filter = 0, RULE_expression = 1, RULE_condition = 2, RULE_comparison = 3, RULE_emptyfield = 4, RULE_filledfield = 5, - RULE_op = 6, RULE_value = 7, RULE_integer = 8, RULE_decimal = 9; + RULE_comparisonop = 6, RULE_value = 7, RULE_pattern = 8, + RULE_patternop = 9, RULE_integer = 10, RULE_decimal = 11; /** * @var array */ public const RULE_NAMES = [ 'filter', 'expression', 'condition', 'comparison', 'emptyfield', 'filledfield', - 'op', 'value', 'integer', 'decimal' + 'comparisonop', 'value', 'pattern', 'patternop', 'integer', 'decimal' ]; /** @@ -45,16 +47,18 @@ final class ApiFilterParser extends Parser */ private const LITERAL_NAMES = [ null, "'='", "'>'", "'>='", "'<'", "'<='", null, null, null, null, - null, null, null, null, null, "'('", "')'", "'.'" + null, null, null, null, null, null, null, null, null, null, null, + "'('", "')'", null, "'.'" ]; /** * @var array */ private const SYMBOLIC_NAMES = [ - null, "EQ", "GT", "GTE", "LT", "LTE", "NEQ", "LIKE", "BOOL", "STRING", - "AND", "OR", "NOT", "IS", "NULL", "OPEN_PAR", "CLOSE_PAR", "DOT", - "INT", "FIELD", "WS" + null, "EQ", "GT", "GTE", "LT", "LTE", "NEQ", "LIKE", "CONTAINS", "STARTSWITH", + "ENDSWITH", "STARTS", "ENDS", "WITH", "BOOL", "STRING", "AND", "OR", + "IS", "NULL", "NOT", "OPEN_PAR", "CLOSE_PAR", "INT", "DOT", "FIELD", + "WS" ]; /** @@ -62,58 +66,65 @@ final class ApiFilterParser extends Parser */ private const SERIALIZED_ATN = "\u{3}\u{608B}\u{A72A}\u{8133}\u{B9ED}\u{417C}\u{3BE7}\u{7786}\u{5964}" . - "\u{3}\u{16}\u{50}\u{4}\u{2}\u{9}\u{2}\u{4}\u{3}\u{9}\u{3}\u{4}\u{4}" . + "\u{3}\u{1C}\u{5B}\u{4}\u{2}\u{9}\u{2}\u{4}\u{3}\u{9}\u{3}\u{4}\u{4}" . "\u{9}\u{4}\u{4}\u{5}\u{9}\u{5}\u{4}\u{6}\u{9}\u{6}\u{4}\u{7}\u{9}" . "\u{7}\u{4}\u{8}\u{9}\u{8}\u{4}\u{9}\u{9}\u{9}\u{4}\u{A}\u{9}\u{A}" . - "\u{4}\u{B}\u{9}\u{B}\u{3}\u{2}\u{3}\u{2}\u{3}\u{2}\u{3}\u{3}\u{3}" . - "\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{5}\u{3}\u{20}\u{A}" . - "\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}" . - "\u{7}\u{3}\u{28}\u{A}\u{3}\u{C}\u{3}\u{E}\u{3}\u{2B}\u{B}\u{3}\u{3}" . - "\u{4}\u{3}\u{4}\u{3}\u{4}\u{5}\u{4}\u{30}\u{A}\u{4}\u{3}\u{5}\u{3}" . - "\u{5}\u{3}\u{5}\u{3}\u{5}\u{3}\u{6}\u{3}\u{6}\u{3}\u{6}\u{3}\u{6}" . - "\u{3}\u{7}\u{3}\u{7}\u{3}\u{7}\u{3}\u{7}\u{5}\u{7}\u{3E}\u{A}\u{7}" . - "\u{3}\u{7}\u{3}\u{7}\u{3}\u{8}\u{3}\u{8}\u{3}\u{9}\u{3}\u{9}\u{3}" . - "\u{9}\u{3}\u{9}\u{5}\u{9}\u{48}\u{A}\u{9}\u{3}\u{A}\u{3}\u{A}\u{3}" . - "\u{B}\u{3}\u{B}\u{3}\u{B}\u{3}\u{B}\u{3}\u{B}\u{2}\u{3}\u{4}\u{C}" . - "\u{2}\u{4}\u{6}\u{8}\u{A}\u{C}\u{E}\u{10}\u{12}\u{14}\u{2}\u{4}\u{4}" . - "\u{2}\u{3}\u{3}\u{F}\u{F}\u{3}\u{2}\u{3}\u{9}\u{2}\u{4E}\u{2}\u{16}" . - "\u{3}\u{2}\u{2}\u{2}\u{4}\u{1F}\u{3}\u{2}\u{2}\u{2}\u{6}\u{2F}\u{3}" . - "\u{2}\u{2}\u{2}\u{8}\u{31}\u{3}\u{2}\u{2}\u{2}\u{A}\u{35}\u{3}\u{2}" . - "\u{2}\u{2}\u{C}\u{39}\u{3}\u{2}\u{2}\u{2}\u{E}\u{41}\u{3}\u{2}\u{2}" . - "\u{2}\u{10}\u{47}\u{3}\u{2}\u{2}\u{2}\u{12}\u{49}\u{3}\u{2}\u{2}\u{2}" . - "\u{14}\u{4B}\u{3}\u{2}\u{2}\u{2}\u{16}\u{17}\u{5}\u{4}\u{3}\u{2}\u{17}" . - "\u{18}\u{7}\u{2}\u{2}\u{3}\u{18}\u{3}\u{3}\u{2}\u{2}\u{2}\u{19}\u{1A}" . - "\u{8}\u{3}\u{1}\u{2}\u{1A}\u{20}\u{5}\u{6}\u{4}\u{2}\u{1B}\u{1C}\u{7}" . - "\u{11}\u{2}\u{2}\u{1C}\u{1D}\u{5}\u{4}\u{3}\u{2}\u{1D}\u{1E}\u{7}" . - "\u{12}\u{2}\u{2}\u{1E}\u{20}\u{3}\u{2}\u{2}\u{2}\u{1F}\u{19}\u{3}" . - "\u{2}\u{2}\u{2}\u{1F}\u{1B}\u{3}\u{2}\u{2}\u{2}\u{20}\u{29}\u{3}\u{2}" . - "\u{2}\u{2}\u{21}\u{22}\u{C}\u{4}\u{2}\u{2}\u{22}\u{23}\u{7}\u{C}\u{2}" . - "\u{2}\u{23}\u{28}\u{5}\u{4}\u{3}\u{5}\u{24}\u{25}\u{C}\u{3}\u{2}\u{2}" . - "\u{25}\u{26}\u{7}\u{D}\u{2}\u{2}\u{26}\u{28}\u{5}\u{4}\u{3}\u{4}\u{27}" . - "\u{21}\u{3}\u{2}\u{2}\u{2}\u{27}\u{24}\u{3}\u{2}\u{2}\u{2}\u{28}\u{2B}" . - "\u{3}\u{2}\u{2}\u{2}\u{29}\u{27}\u{3}\u{2}\u{2}\u{2}\u{29}\u{2A}\u{3}" . - "\u{2}\u{2}\u{2}\u{2A}\u{5}\u{3}\u{2}\u{2}\u{2}\u{2B}\u{29}\u{3}\u{2}" . - "\u{2}\u{2}\u{2C}\u{30}\u{5}\u{8}\u{5}\u{2}\u{2D}\u{30}\u{5}\u{A}\u{6}" . - "\u{2}\u{2E}\u{30}\u{5}\u{C}\u{7}\u{2}\u{2F}\u{2C}\u{3}\u{2}\u{2}\u{2}" . - "\u{2F}\u{2D}\u{3}\u{2}\u{2}\u{2}\u{2F}\u{2E}\u{3}\u{2}\u{2}\u{2}\u{30}" . - "\u{7}\u{3}\u{2}\u{2}\u{2}\u{31}\u{32}\u{7}\u{15}\u{2}\u{2}\u{32}\u{33}" . - "\u{5}\u{E}\u{8}\u{2}\u{33}\u{34}\u{5}\u{10}\u{9}\u{2}\u{34}\u{9}\u{3}" . - "\u{2}\u{2}\u{2}\u{35}\u{36}\u{7}\u{15}\u{2}\u{2}\u{36}\u{37}\u{9}" . - "\u{2}\u{2}\u{2}\u{37}\u{38}\u{7}\u{10}\u{2}\u{2}\u{38}\u{B}\u{3}\u{2}" . - "\u{2}\u{2}\u{39}\u{3D}\u{7}\u{15}\u{2}\u{2}\u{3A}\u{3E}\u{7}\u{8}" . - "\u{2}\u{2}\u{3B}\u{3C}\u{7}\u{F}\u{2}\u{2}\u{3C}\u{3E}\u{7}\u{E}\u{2}" . - "\u{2}\u{3D}\u{3A}\u{3}\u{2}\u{2}\u{2}\u{3D}\u{3B}\u{3}\u{2}\u{2}\u{2}" . - "\u{3E}\u{3F}\u{3}\u{2}\u{2}\u{2}\u{3F}\u{40}\u{7}\u{10}\u{2}\u{2}" . - "\u{40}\u{D}\u{3}\u{2}\u{2}\u{2}\u{41}\u{42}\u{9}\u{3}\u{2}\u{2}\u{42}" . - "\u{F}\u{3}\u{2}\u{2}\u{2}\u{43}\u{48}\u{7}\u{A}\u{2}\u{2}\u{44}\u{48}" . - "\u{7}\u{B}\u{2}\u{2}\u{45}\u{48}\u{5}\u{12}\u{A}\u{2}\u{46}\u{48}" . - "\u{5}\u{14}\u{B}\u{2}\u{47}\u{43}\u{3}\u{2}\u{2}\u{2}\u{47}\u{44}" . - "\u{3}\u{2}\u{2}\u{2}\u{47}\u{45}\u{3}\u{2}\u{2}\u{2}\u{47}\u{46}\u{3}" . - "\u{2}\u{2}\u{2}\u{48}\u{11}\u{3}\u{2}\u{2}\u{2}\u{49}\u{4A}\u{7}\u{14}" . - "\u{2}\u{2}\u{4A}\u{13}\u{3}\u{2}\u{2}\u{2}\u{4B}\u{4C}\u{7}\u{14}" . - "\u{2}\u{2}\u{4C}\u{4D}\u{7}\u{13}\u{2}\u{2}\u{4D}\u{4E}\u{7}\u{14}" . - "\u{2}\u{2}\u{4E}\u{15}\u{3}\u{2}\u{2}\u{2}\u{8}\u{1F}\u{27}\u{29}" . - "\u{2F}\u{3D}\u{47}"; + "\u{4}\u{B}\u{9}\u{B}\u{4}\u{C}\u{9}\u{C}\u{4}\u{D}\u{9}\u{D}\u{3}" . + "\u{2}\u{3}\u{2}\u{3}\u{2}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}" . + "\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{5}\u{3}\u{25}\u{A}\u{3}\u{3}\u{3}" . + "\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{3}\u{7}\u{3}\u{2D}" . + "\u{A}\u{3}\u{C}\u{3}\u{E}\u{3}\u{30}\u{B}\u{3}\u{3}\u{4}\u{3}\u{4}" . + "\u{3}\u{4}\u{5}\u{4}\u{35}\u{A}\u{4}\u{3}\u{5}\u{3}\u{5}\u{3}\u{5}" . + "\u{3}\u{5}\u{3}\u{6}\u{3}\u{6}\u{3}\u{6}\u{3}\u{6}\u{3}\u{7}\u{3}" . + "\u{7}\u{3}\u{7}\u{3}\u{7}\u{5}\u{7}\u{43}\u{A}\u{7}\u{3}\u{7}\u{3}" . + "\u{7}\u{3}\u{8}\u{3}\u{8}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}\u{3}\u{9}" . + "\u{5}\u{9}\u{4D}\u{A}\u{9}\u{3}\u{A}\u{3}\u{A}\u{3}\u{A}\u{3}\u{A}" . + "\u{3}\u{B}\u{3}\u{B}\u{3}\u{C}\u{3}\u{C}\u{3}\u{D}\u{3}\u{D}\u{3}" . + "\u{D}\u{3}\u{D}\u{3}\u{D}\u{2}\u{3}\u{4}\u{E}\u{2}\u{4}\u{6}\u{8}" . + "\u{A}\u{C}\u{E}\u{10}\u{12}\u{14}\u{16}\u{18}\u{2}\u{5}\u{4}\u{2}" . + "\u{3}\u{3}\u{14}\u{14}\u{3}\u{2}\u{3}\u{8}\u{3}\u{2}\u{9}\u{C}\u{2}" . + "\u{58}\u{2}\u{1A}\u{3}\u{2}\u{2}\u{2}\u{4}\u{24}\u{3}\u{2}\u{2}\u{2}" . + "\u{6}\u{34}\u{3}\u{2}\u{2}\u{2}\u{8}\u{36}\u{3}\u{2}\u{2}\u{2}\u{A}" . + "\u{3A}\u{3}\u{2}\u{2}\u{2}\u{C}\u{3E}\u{3}\u{2}\u{2}\u{2}\u{E}\u{46}" . + "\u{3}\u{2}\u{2}\u{2}\u{10}\u{4C}\u{3}\u{2}\u{2}\u{2}\u{12}\u{4E}\u{3}" . + "\u{2}\u{2}\u{2}\u{14}\u{52}\u{3}\u{2}\u{2}\u{2}\u{16}\u{54}\u{3}\u{2}" . + "\u{2}\u{2}\u{18}\u{56}\u{3}\u{2}\u{2}\u{2}\u{1A}\u{1B}\u{5}\u{4}\u{3}" . + "\u{2}\u{1B}\u{1C}\u{7}\u{2}\u{2}\u{3}\u{1C}\u{3}\u{3}\u{2}\u{2}\u{2}" . + "\u{1D}\u{1E}\u{8}\u{3}\u{1}\u{2}\u{1E}\u{25}\u{5}\u{6}\u{4}\u{2}\u{1F}" . + "\u{25}\u{5}\u{12}\u{A}\u{2}\u{20}\u{21}\u{7}\u{17}\u{2}\u{2}\u{21}" . + "\u{22}\u{5}\u{4}\u{3}\u{2}\u{22}\u{23}\u{7}\u{18}\u{2}\u{2}\u{23}" . + "\u{25}\u{3}\u{2}\u{2}\u{2}\u{24}\u{1D}\u{3}\u{2}\u{2}\u{2}\u{24}\u{1F}" . + "\u{3}\u{2}\u{2}\u{2}\u{24}\u{20}\u{3}\u{2}\u{2}\u{2}\u{25}\u{2E}\u{3}" . + "\u{2}\u{2}\u{2}\u{26}\u{27}\u{C}\u{4}\u{2}\u{2}\u{27}\u{28}\u{7}\u{12}" . + "\u{2}\u{2}\u{28}\u{2D}\u{5}\u{4}\u{3}\u{5}\u{29}\u{2A}\u{C}\u{3}\u{2}" . + "\u{2}\u{2A}\u{2B}\u{7}\u{13}\u{2}\u{2}\u{2B}\u{2D}\u{5}\u{4}\u{3}" . + "\u{4}\u{2C}\u{26}\u{3}\u{2}\u{2}\u{2}\u{2C}\u{29}\u{3}\u{2}\u{2}\u{2}" . + "\u{2D}\u{30}\u{3}\u{2}\u{2}\u{2}\u{2E}\u{2C}\u{3}\u{2}\u{2}\u{2}\u{2E}" . + "\u{2F}\u{3}\u{2}\u{2}\u{2}\u{2F}\u{5}\u{3}\u{2}\u{2}\u{2}\u{30}\u{2E}" . + "\u{3}\u{2}\u{2}\u{2}\u{31}\u{35}\u{5}\u{8}\u{5}\u{2}\u{32}\u{35}\u{5}" . + "\u{A}\u{6}\u{2}\u{33}\u{35}\u{5}\u{C}\u{7}\u{2}\u{34}\u{31}\u{3}\u{2}" . + "\u{2}\u{2}\u{34}\u{32}\u{3}\u{2}\u{2}\u{2}\u{34}\u{33}\u{3}\u{2}\u{2}" . + "\u{2}\u{35}\u{7}\u{3}\u{2}\u{2}\u{2}\u{36}\u{37}\u{7}\u{1B}\u{2}\u{2}" . + "\u{37}\u{38}\u{5}\u{E}\u{8}\u{2}\u{38}\u{39}\u{5}\u{10}\u{9}\u{2}" . + "\u{39}\u{9}\u{3}\u{2}\u{2}\u{2}\u{3A}\u{3B}\u{7}\u{1B}\u{2}\u{2}\u{3B}" . + "\u{3C}\u{9}\u{2}\u{2}\u{2}\u{3C}\u{3D}\u{7}\u{15}\u{2}\u{2}\u{3D}" . + "\u{B}\u{3}\u{2}\u{2}\u{2}\u{3E}\u{42}\u{7}\u{1B}\u{2}\u{2}\u{3F}\u{43}" . + "\u{7}\u{8}\u{2}\u{2}\u{40}\u{41}\u{7}\u{14}\u{2}\u{2}\u{41}\u{43}" . + "\u{7}\u{16}\u{2}\u{2}\u{42}\u{3F}\u{3}\u{2}\u{2}\u{2}\u{42}\u{40}" . + "\u{3}\u{2}\u{2}\u{2}\u{43}\u{44}\u{3}\u{2}\u{2}\u{2}\u{44}\u{45}\u{7}" . + "\u{15}\u{2}\u{2}\u{45}\u{D}\u{3}\u{2}\u{2}\u{2}\u{46}\u{47}\u{9}\u{3}" . + "\u{2}\u{2}\u{47}\u{F}\u{3}\u{2}\u{2}\u{2}\u{48}\u{4D}\u{7}\u{10}\u{2}" . + "\u{2}\u{49}\u{4D}\u{7}\u{11}\u{2}\u{2}\u{4A}\u{4D}\u{5}\u{16}\u{C}" . + "\u{2}\u{4B}\u{4D}\u{5}\u{18}\u{D}\u{2}\u{4C}\u{48}\u{3}\u{2}\u{2}" . + "\u{2}\u{4C}\u{49}\u{3}\u{2}\u{2}\u{2}\u{4C}\u{4A}\u{3}\u{2}\u{2}\u{2}" . + "\u{4C}\u{4B}\u{3}\u{2}\u{2}\u{2}\u{4D}\u{11}\u{3}\u{2}\u{2}\u{2}\u{4E}" . + "\u{4F}\u{7}\u{1B}\u{2}\u{2}\u{4F}\u{50}\u{5}\u{14}\u{B}\u{2}\u{50}" . + "\u{51}\u{7}\u{11}\u{2}\u{2}\u{51}\u{13}\u{3}\u{2}\u{2}\u{2}\u{52}" . + "\u{53}\u{9}\u{4}\u{2}\u{2}\u{53}\u{15}\u{3}\u{2}\u{2}\u{2}\u{54}\u{55}" . + "\u{7}\u{19}\u{2}\u{2}\u{55}\u{17}\u{3}\u{2}\u{2}\u{2}\u{56}\u{57}" . + "\u{7}\u{19}\u{2}\u{2}\u{57}\u{58}\u{7}\u{1A}\u{2}\u{2}\u{58}\u{59}" . + "\u{7}\u{19}\u{2}\u{2}\u{59}\u{19}\u{3}\u{2}\u{2}\u{2}\u{8}\u{24}\u{2C}" . + "\u{2E}\u{34}\u{42}\u{4C}"; protected static $atn; protected static $decisionToDFA; @@ -186,9 +197,9 @@ public function filter() : Context\FilterContext try { $this->enterOuterAlt($localContext, 1); - $this->setState(20); + $this->setState(24); $this->recursiveExpression(0); - $this->setState(21); + $this->setState(25); $this->match(self::EOF); } catch (RecognitionException $exception) { $localContext->exception = $exception; @@ -223,36 +234,41 @@ private function recursiveExpression(int $precedence) : Context\ExpressionContex try { $this->enterOuterAlt($localContext, 1); - $this->setState(29); + $this->setState(34); $this->errorHandler->sync($this); - switch ($this->input->LA(1)) { - case self::FIELD: - $localContext = new Context\ConditionExpContext($localContext); - $this->ctx = $localContext; - $previousContext = $localContext; - - $this->setState(24); - $this->condition(); - break; - - case self::OPEN_PAR: - $localContext = new Context\ParenthesisExpContext($localContext); - $this->ctx = $localContext; - $previousContext = $localContext; - $this->setState(25); - $this->match(self::OPEN_PAR); - $this->setState(26); - $this->recursiveExpression(0); - $this->setState(27); - $this->match(self::CLOSE_PAR); - break; - - default: - throw new NoViableAltException($this); + switch ($this->getInterpreter()->adaptivePredict($this->input, 0, $this->ctx)) { + case 1: + $localContext = new Context\ConditionExpContext($localContext); + $this->ctx = $localContext; + $previousContext = $localContext; + + $this->setState(28); + $this->condition(); + break; + + case 2: + $localContext = new Context\PatternExpContext($localContext); + $this->ctx = $localContext; + $previousContext = $localContext; + $this->setState(29); + $this->pattern(); + break; + + case 3: + $localContext = new Context\ParenthesisExpContext($localContext); + $this->ctx = $localContext; + $previousContext = $localContext; + $this->setState(30); + $this->match(self::OPEN_PAR); + $this->setState(31); + $this->recursiveExpression(0); + $this->setState(32); + $this->match(self::CLOSE_PAR); + break; } $this->ctx->stop = $this->input->LT(-1); - $this->setState(39); + $this->setState(44); $this->errorHandler->sync($this); $alt = $this->getInterpreter()->adaptivePredict($this->input, 2, $this->ctx); @@ -264,41 +280,41 @@ private function recursiveExpression(int $precedence) : Context\ExpressionContex } $previousContext = $localContext; - $this->setState(37); + $this->setState(42); $this->errorHandler->sync($this); switch ($this->getInterpreter()->adaptivePredict($this->input, 1, $this->ctx)) { case 1: $localContext = new Context\ConjunctionExpContext(new Context\ExpressionContext($parentContext, $parentState)); $this->pushNewRecursionContext($localContext, $startState, self::RULE_expression); - $this->setState(31); + $this->setState(36); if (!($this->precpred($this->ctx, 2))) { throw new FailedPredicateException($this, "\\\$this->precpred(\\\$this->ctx, 2)"); } - $this->setState(32); + $this->setState(37); $this->match(self::AND); - $this->setState(33); + $this->setState(38); $this->recursiveExpression(3); break; case 2: $localContext = new Context\DisjunctionExpContext(new Context\ExpressionContext($parentContext, $parentState)); $this->pushNewRecursionContext($localContext, $startState, self::RULE_expression); - $this->setState(34); + $this->setState(39); if (!($this->precpred($this->ctx, 1))) { throw new FailedPredicateException($this, "\\\$this->precpred(\\\$this->ctx, 1)"); } - $this->setState(35); + $this->setState(40); $this->match(self::OR); - $this->setState(36); + $this->setState(41); $this->recursiveExpression(2); break; } } - $this->setState(41); + $this->setState(46); $this->errorHandler->sync($this); $alt = $this->getInterpreter()->adaptivePredict($this->input, 2, $this->ctx); @@ -324,28 +340,28 @@ public function condition() : Context\ConditionContext $this->enterRule($localContext, 4, self::RULE_condition); try { - $this->setState(45); + $this->setState(50); $this->errorHandler->sync($this); switch ($this->getInterpreter()->adaptivePredict($this->input, 3, $this->ctx)) { case 1: $localContext = new Context\ComparisonConditionContext($localContext); $this->enterOuterAlt($localContext, 1); - $this->setState(42); + $this->setState(47); $this->comparison(); break; case 2: $localContext = new Context\EmptyConditionContext($localContext); $this->enterOuterAlt($localContext, 2); - $this->setState(43); + $this->setState(48); $this->emptyfield(); break; case 3: $localContext = new Context\FilledConditionContext($localContext); $this->enterOuterAlt($localContext, 3); - $this->setState(44); + $this->setState(49); $this->filledfield(); break; } @@ -371,11 +387,11 @@ public function comparison() : Context\ComparisonContext try { $this->enterOuterAlt($localContext, 1); - $this->setState(47); + $this->setState(52); $this->match(self::FIELD); - $this->setState(48); - $this->op(); - $this->setState(49); + $this->setState(53); + $this->comparisonop(); + $this->setState(54); $this->value(); } catch (RecognitionException $exception) { $localContext->exception = $exception; @@ -399,9 +415,9 @@ public function emptyfield() : Context\EmptyfieldContext try { $this->enterOuterAlt($localContext, 1); - $this->setState(51); + $this->setState(56); $this->match(self::FIELD); - $this->setState(52); + $this->setState(57); $_la = $this->input->LA(1); @@ -415,7 +431,7 @@ public function emptyfield() : Context\EmptyfieldContext $this->errorHandler->reportMatch($this); $this->consume(); } - $this->setState(53); + $this->setState(58); $this->match(self::NULL); } catch (RecognitionException $exception) { $localContext->exception = $exception; @@ -439,28 +455,28 @@ public function filledfield() : Context\FilledfieldContext try { $this->enterOuterAlt($localContext, 1); - $this->setState(55); + $this->setState(60); $this->match(self::FIELD); - $this->setState(59); + $this->setState(64); $this->errorHandler->sync($this); switch ($this->input->LA(1)) { case self::NEQ: - $this->setState(56); + $this->setState(61); $this->match(self::NEQ); break; case self::IS: - $this->setState(57); + $this->setState(62); $this->match(self::IS); - $this->setState(58); + $this->setState(63); $this->match(self::NOT); break; default: throw new NoViableAltException($this); } - $this->setState(61); + $this->setState(66); $this->match(self::NULL); } catch (RecognitionException $exception) { $localContext->exception = $exception; @@ -476,19 +492,19 @@ public function filledfield() : Context\FilledfieldContext /** * @throws RecognitionException */ - public function op() : Context\OpContext + public function comparisonop() : Context\ComparisonopContext { - $localContext = new Context\OpContext($this->ctx, $this->getState()); + $localContext = new Context\ComparisonopContext($this->ctx, $this->getState()); - $this->enterRule($localContext, 12, self::RULE_op); + $this->enterRule($localContext, 12, self::RULE_comparisonop); try { $this->enterOuterAlt($localContext, 1); - $this->setState(63); + $this->setState(68); $_la = $this->input->LA(1); - if (!(((($_la) & ~0x3f) === 0 && ((1 << $_la) & ((1 << self::EQ) | (1 << self::GT) | (1 << self::GTE) | (1 << self::LT) | (1 << self::LTE) | (1 << self::NEQ) | (1 << self::LIKE))) !== 0))) { + if (!(((($_la) & ~0x3f) === 0 && ((1 << $_la) & ((1 << self::EQ) | (1 << self::GT) | (1 << self::GTE) | (1 << self::LT) | (1 << self::LTE) | (1 << self::NEQ))) !== 0))) { $this->errorHandler->recoverInline($this); } else { if ($this->input->LA(1) === Token::EOF) { @@ -520,27 +536,27 @@ public function value() : Context\ValueContext try { $this->enterOuterAlt($localContext, 1); - $this->setState(69); + $this->setState(74); $this->errorHandler->sync($this); switch ($this->getInterpreter()->adaptivePredict($this->input, 5, $this->ctx)) { case 1: - $this->setState(65); + $this->setState(70); $this->match(self::BOOL); break; case 2: - $this->setState(66); + $this->setState(71); $this->match(self::STRING); break; case 3: - $this->setState(67); + $this->setState(72); $this->integer(); break; case 4: - $this->setState(68); + $this->setState(73); $this->decimal(); break; } @@ -555,6 +571,70 @@ public function value() : Context\ValueContext return $localContext; } + /** + * @throws RecognitionException + */ + public function pattern() : Context\PatternContext + { + $localContext = new Context\PatternContext($this->ctx, $this->getState()); + + $this->enterRule($localContext, 16, self::RULE_pattern); + + try { + $this->enterOuterAlt($localContext, 1); + $this->setState(76); + $this->match(self::FIELD); + $this->setState(77); + $this->patternop(); + $this->setState(78); + $this->match(self::STRING); + } catch (RecognitionException $exception) { + $localContext->exception = $exception; + $this->errorHandler->reportError($this, $exception); + $this->errorHandler->recover($this, $exception); + } finally { + $this->exitRule(); + } + + return $localContext; + } + + /** + * @throws RecognitionException + */ + public function patternop() : Context\PatternopContext + { + $localContext = new Context\PatternopContext($this->ctx, $this->getState()); + + $this->enterRule($localContext, 18, self::RULE_patternop); + + try { + $this->enterOuterAlt($localContext, 1); + $this->setState(80); + + $_la = $this->input->LA(1); + + if (!(((($_la) & ~0x3f) === 0 && ((1 << $_la) & ((1 << self::LIKE) | (1 << self::CONTAINS) | (1 << self::STARTSWITH) | (1 << self::ENDSWITH))) !== 0))) { + $this->errorHandler->recoverInline($this); + } else { + if ($this->input->LA(1) === Token::EOF) { + $this->matchedEOF = true; + } + + $this->errorHandler->reportMatch($this); + $this->consume(); + } + } catch (RecognitionException $exception) { + $localContext->exception = $exception; + $this->errorHandler->reportError($this, $exception); + $this->errorHandler->recover($this, $exception); + } finally { + $this->exitRule(); + } + + return $localContext; + } + /** * @throws RecognitionException */ @@ -562,11 +642,11 @@ public function integer() : Context\IntegerContext { $localContext = new Context\IntegerContext($this->ctx, $this->getState()); - $this->enterRule($localContext, 16, self::RULE_integer); + $this->enterRule($localContext, 20, self::RULE_integer); try { $this->enterOuterAlt($localContext, 1); - $this->setState(71); + $this->setState(82); $this->match(self::INT); } catch (RecognitionException $exception) { $localContext->exception = $exception; @@ -586,15 +666,15 @@ public function decimal() : Context\DecimalContext { $localContext = new Context\DecimalContext($this->ctx, $this->getState()); - $this->enterRule($localContext, 18, self::RULE_decimal); + $this->enterRule($localContext, 22, self::RULE_decimal); try { $this->enterOuterAlt($localContext, 1); - $this->setState(73); + $this->setState(84); $this->match(self::INT); - $this->setState(74); + $this->setState(85); $this->match(self::DOT); - $this->setState(75); + $this->setState(86); $this->match(self::INT); } catch (RecognitionException $exception) { $localContext->exception = $exception; @@ -693,6 +773,30 @@ public function copyFrom(ParserRuleContext $context) : void } } + class PatternExpContext extends ExpressionContext + { + public function __construct(ExpressionContext $context) + { + parent::__construct($context); + + $this->copyFrom($context); + } + + public function pattern() : ?PatternContext + { + return $this->getTypedRuleContext(PatternContext::class, 0); + } + + public function accept(ParseTreeVisitor $visitor) + { + if ($visitor instanceof ApiFilterVisitor) { + return $visitor->visitPatternExp($this); + } + + return $visitor->visitChildren($this); + } + } + class ConjunctionExpContext extends ExpressionContext { public function __construct(ExpressionContext $context) @@ -931,9 +1035,9 @@ public function FIELD() : ?TerminalNode return $this->getToken(ApiFilterParser::FIELD, 0); } - public function op() : ?OpContext + public function comparisonop() : ?ComparisonopContext { - return $this->getTypedRuleContext(OpContext::class, 0); + return $this->getTypedRuleContext(ComparisonopContext::class, 0); } public function value() : ?ValueContext @@ -1040,7 +1144,7 @@ public function accept(ParseTreeVisitor $visitor) } } - class OpContext extends ParserRuleContext + class ComparisonopContext extends ParserRuleContext { public function __construct(?ParserRuleContext $parent, ?int $invokingState = null) { @@ -1049,7 +1153,7 @@ public function __construct(?ParserRuleContext $parent, ?int $invokingState = nu public function getRuleIndex() : int { - return ApiFilterParser::RULE_op; + return ApiFilterParser::RULE_comparisonop; } public function EQ() : ?TerminalNode @@ -1082,15 +1186,10 @@ public function NEQ() : ?TerminalNode return $this->getToken(ApiFilterParser::NEQ, 0); } - public function LIKE() : ?TerminalNode - { - return $this->getToken(ApiFilterParser::LIKE, 0); - } - public function accept(ParseTreeVisitor $visitor) { if ($visitor instanceof ApiFilterVisitor) { - return $visitor->visitOp($this); + return $visitor->visitComparisonop($this); } return $visitor->visitChildren($this); @@ -1139,6 +1238,85 @@ public function accept(ParseTreeVisitor $visitor) } } + class PatternContext extends ParserRuleContext + { + public function __construct(?ParserRuleContext $parent, ?int $invokingState = null) + { + parent::__construct($parent, $invokingState); + } + + public function getRuleIndex() : int + { + return ApiFilterParser::RULE_pattern; + } + + public function FIELD() : ?TerminalNode + { + return $this->getToken(ApiFilterParser::FIELD, 0); + } + + public function patternop() : ?PatternopContext + { + return $this->getTypedRuleContext(PatternopContext::class, 0); + } + + public function STRING() : ?TerminalNode + { + return $this->getToken(ApiFilterParser::STRING, 0); + } + + public function accept(ParseTreeVisitor $visitor) + { + if ($visitor instanceof ApiFilterVisitor) { + return $visitor->visitPattern($this); + } + + return $visitor->visitChildren($this); + } + } + + class PatternopContext extends ParserRuleContext + { + public function __construct(?ParserRuleContext $parent, ?int $invokingState = null) + { + parent::__construct($parent, $invokingState); + } + + public function getRuleIndex() : int + { + return ApiFilterParser::RULE_patternop; + } + + public function LIKE() : ?TerminalNode + { + return $this->getToken(ApiFilterParser::LIKE, 0); + } + + public function CONTAINS() : ?TerminalNode + { + return $this->getToken(ApiFilterParser::CONTAINS, 0); + } + + public function STARTSWITH() : ?TerminalNode + { + return $this->getToken(ApiFilterParser::STARTSWITH, 0); + } + + public function ENDSWITH() : ?TerminalNode + { + return $this->getToken(ApiFilterParser::ENDSWITH, 0); + } + + public function accept(ParseTreeVisitor $visitor) + { + if ($visitor instanceof ApiFilterVisitor) { + return $visitor->visitPatternop($this); + } + + return $visitor->visitChildren($this); + } + } + class IntegerContext extends ParserRuleContext { public function __construct(?ParserRuleContext $parent, ?int $invokingState = null) diff --git a/src/ApiFilter/Parser/ApiFilterVisitor.php b/src/ApiFilter/Parser/ApiFilterVisitor.php index 0fc47b0..c682c86 100644 --- a/src/ApiFilter/Parser/ApiFilterVisitor.php +++ b/src/ApiFilter/Parser/ApiFilterVisitor.php @@ -22,6 +22,16 @@ interface ApiFilterVisitor extends ParseTreeVisitor */ public function visitFilter(Context\FilterContext $context); + /** + * Visit a parse tree produced by the `patternExp` labeled alternative + * in {@see ApiFilterParser::expression()}. + * + * @param Context\PatternExpContext $context The parse tree. + * + * @return mixed The visitor result. + */ + public function visitPatternExp(Context\PatternExpContext $context); + /** * Visit a parse tree produced by the `conjunctionExp` labeled alternative * in {@see ApiFilterParser::expression()}. @@ -120,13 +130,13 @@ public function visitEmptyfield(Context\EmptyfieldContext $context); public function visitFilledfield(Context\FilledfieldContext $context); /** - * Visit a parse tree produced by {@see ApiFilterParser::op()}. + * Visit a parse tree produced by {@see ApiFilterParser::comparisonop()}. * - * @param Context\OpContext $context The parse tree. + * @param Context\ComparisonopContext $context The parse tree. * * @return mixed The visitor result. */ - public function visitOp(Context\OpContext $context); + public function visitComparisonop(Context\ComparisonopContext $context); /** * Visit a parse tree produced by {@see ApiFilterParser::value()}. @@ -137,6 +147,24 @@ public function visitOp(Context\OpContext $context); */ public function visitValue(Context\ValueContext $context); + /** + * Visit a parse tree produced by {@see ApiFilterParser::pattern()}. + * + * @param Context\PatternContext $context The parse tree. + * + * @return mixed The visitor result. + */ + public function visitPattern(Context\PatternContext $context); + + /** + * Visit a parse tree produced by {@see ApiFilterParser::patternop()}. + * + * @param Context\PatternopContext $context The parse tree. + * + * @return mixed The visitor result. + */ + public function visitPatternop(Context\PatternopContext $context); + /** * Visit a parse tree produced by {@see ApiFilterParser::integer()}. * diff --git a/tests/ApiFilter/Filter/ComparisonTest.php b/tests/ApiFilter/Filter/ComparisonTest.php index b4654e9..f12ecc3 100644 --- a/tests/ApiFilter/Filter/ComparisonTest.php +++ b/tests/ApiFilter/Filter/ComparisonTest.php @@ -39,7 +39,7 @@ public static function tearDownAfterClass(): void */ public function testValue() { - $comparison = new Comparison("city", Operator::EQ, "Bergamo"); + $comparison = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); $this->assertEquals("Bergamo", $comparison->getValue()); $comparison->setValue(5); @@ -57,7 +57,7 @@ public function testValue() */ public function testField() { - $comparison = new Comparison("name", Operator::EQ, "Bergamo"); + $comparison = new Comparison("name", ComparisonOperator::EQ, "Bergamo"); $this->assertEquals("name", $comparison->getField()); $comparison->setField("city"); @@ -69,26 +69,23 @@ public function testField() */ public function testOperator() { - $comparison = new Comparison("city", Operator::EQ, "Bergamo"); - $this->assertEquals(Operator::EQ, $comparison->getOp()); + $comparison = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); + $this->assertEquals(ComparisonOperator::EQ, $comparison->getOp()); - $comparison->setOp(Operator::GT); - $this->assertEquals(Operator::GT, $comparison->getOp()); + $comparison->setOp(ComparisonOperator::GT); + $this->assertEquals(ComparisonOperator::GT, $comparison->getOp()); - $comparison->setOp(Operator::GTE); - $this->assertEquals(Operator::GTE, $comparison->getOp()); + $comparison->setOp(ComparisonOperator::GTE); + $this->assertEquals(ComparisonOperator::GTE, $comparison->getOp()); - $comparison->setOp(Operator::LT); - $this->assertEquals(Operator::LT, $comparison->getOp()); + $comparison->setOp(ComparisonOperator::LT); + $this->assertEquals(ComparisonOperator::LT, $comparison->getOp()); - $comparison->setOp(Operator::LTE); - $this->assertEquals(Operator::LTE, $comparison->getOp()); + $comparison->setOp(ComparisonOperator::LTE); + $this->assertEquals(ComparisonOperator::LTE, $comparison->getOp()); - $comparison->setOp(Operator::NEQ); - $this->assertEquals(Operator::NEQ, $comparison->getOp()); - - $comparison->setOp(Operator::LIKE); - $this->assertEquals(Operator::LIKE, $comparison->getOp()); + $comparison->setOp(ComparisonOperator::NEQ); + $this->assertEquals(ComparisonOperator::NEQ, $comparison->getOp()); } /** @@ -96,7 +93,7 @@ public function testOperator() */ public function testToString() { - $comparison = new Comparison("city", Operator::EQ, "Bergamo"); + $comparison = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); $this->assertEquals("city = Bergamo", (string)$comparison); } } diff --git a/tests/ApiFilter/Filter/ConjunctionTest.php b/tests/ApiFilter/Filter/ConjunctionTest.php index b177136..3979f1c 100644 --- a/tests/ApiFilter/Filter/ConjunctionTest.php +++ b/tests/ApiFilter/Filter/ConjunctionTest.php @@ -40,18 +40,18 @@ public static function tearDownAfterClass(): void */ public function testComparisons() { - $left = new Comparison("city", Operator::EQ, "Bergamo"); - $right = new Comparison("age", Operator::LT, 30); + $left = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); + $right = new Comparison("age", ComparisonOperator::LT, 30); $conjunction = new Conjunction($left, $right); $this->assertEquals($left, $conjunction->getLeft()); $this->assertEquals($right, $conjunction->getRight()); - $left2 = new Comparison("state", Operator::NEQ, "USA"); + $left2 = new Comparison("state", ComparisonOperator::NEQ, "USA"); $conjunction->setLeft($left2); $this->assertEquals($left2, $conjunction->getLeft()); $this->assertEquals($right, $conjunction->getRight()); - $right2 = new Comparison("is_single", Operator::EQ, true); + $right2 = new Comparison("is_single", ComparisonOperator::EQ, true); $conjunction->setRight($right2); $this->assertEquals($left2, $conjunction->getLeft()); $this->assertEquals($right2, $conjunction->getRight()); @@ -62,8 +62,8 @@ public function testComparisons() */ public function testToString() { - $left = new Comparison("city", Operator::EQ, "Bergamo"); - $right = new Comparison("age", Operator::LT, 30); + $left = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); + $right = new Comparison("age", ComparisonOperator::LT, 30); $conjunction = new Conjunction($left, $right); $this->assertEquals("CONJUNCTION{ city = Bergamo, age < 30 }", (string)$conjunction); } diff --git a/tests/ApiFilter/Filter/DisjunctionTest.php b/tests/ApiFilter/Filter/DisjunctionTest.php index c00378c..fd4dcd1 100644 --- a/tests/ApiFilter/Filter/DisjunctionTest.php +++ b/tests/ApiFilter/Filter/DisjunctionTest.php @@ -40,18 +40,18 @@ public static function tearDownAfterClass(): void */ public function testComparisons() { - $left = new Comparison("city", Operator::EQ, "Bergamo"); - $right = new Comparison("age", Operator::LT, 30); + $left = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); + $right = new Comparison("age", ComparisonOperator::LT, 30); $disjunction = new Disjunction($left, $right); $this->assertEquals($left, $disjunction->getLeft()); $this->assertEquals($right, $disjunction->getRight()); - $left2 = new Comparison("state", Operator::NEQ, "USA"); + $left2 = new Comparison("state", ComparisonOperator::NEQ, "USA"); $disjunction->setLeft($left2); $this->assertEquals($left2, $disjunction->getLeft()); $this->assertEquals($right, $disjunction->getRight()); - $right2 = new Comparison("is_single", Operator::EQ, true); + $right2 = new Comparison("is_single", ComparisonOperator::EQ, true); $disjunction->setRight($right2); $this->assertEquals($left2, $disjunction->getLeft()); $this->assertEquals($right2, $disjunction->getRight()); @@ -62,8 +62,8 @@ public function testComparisons() */ public function testToString() { - $left = new Comparison("city", Operator::EQ, "Bergamo"); - $right = new Comparison("age", Operator::LT, 30); + $left = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); + $right = new Comparison("age", ComparisonOperator::LT, 30); $disjunction = new Disjunction($left, $right); $this->assertEquals("DISJUNCTION{ city = Bergamo, age < 30 }", (string)$disjunction); } diff --git a/tests/ApiFilter/Filter/FilterTest.php b/tests/ApiFilter/Filter/FilterTest.php index f7e8fd3..0c35fb6 100644 --- a/tests/ApiFilter/Filter/FilterTest.php +++ b/tests/ApiFilter/Filter/FilterTest.php @@ -39,11 +39,11 @@ public static function tearDownAfterClass(): void */ public function testExpression() { - $condition = new Comparison("city", Operator::EQ, "Bergamo"); + $condition = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); $filter = new Filter($condition); $this->assertEquals($condition, $filter->getExpression()); - $condition2 = new Comparison("vacation", Operator::EQ, "Martinica"); + $condition2 = new Comparison("vacation", ComparisonOperator::EQ, "Martinica"); $filter->setExpression($condition2); $this->assertEquals($condition2, $filter->getExpression()); } @@ -53,7 +53,7 @@ public function testExpression() */ public function testToString() { - $condition = new Comparison("city", Operator::EQ, "Bergamo"); + $condition = new Comparison("city", ComparisonOperator::EQ, "Bergamo"); $filter = new Filter($condition); $this->assertEquals("FILTER{ city = Bergamo }", (string)$filter); } diff --git a/tests/ApiFilter/Filter/PatternTest.php b/tests/ApiFilter/Filter/PatternTest.php new file mode 100644 index 0000000..573db76 --- /dev/null +++ b/tests/ApiFilter/Filter/PatternTest.php @@ -0,0 +1,87 @@ +assertEquals("%ergam%", $like->getValue()); + + $like->setValue("%ancouve%"); + $this->assertEquals("%ancouve%", $like->getValue()); + } + + /** + * Test field + */ + public function testField() + { + $comparison = new Pattern("name", PatternOperator::LIKE, "%ergam%"); + $this->assertEquals("name", $comparison->getField()); + + $comparison->setField("city"); + $this->assertEquals("city", $comparison->getField()); + } + + /** + * Test operator + */ + public function testOperator() + { + $pattern = new Pattern("city", PatternOperator::LIKE, "%ergam%"); + $this->assertEquals(PatternOperator::LIKE, $pattern->getOp()); + + $pattern->setOp(PatternOperator::CONTAINS); + $this->assertEquals(PatternOperator::CONTAINS, $pattern->getOp()); + + $pattern->setOp(PatternOperator::STARTS_WITH); + $this->assertEquals(PatternOperator::STARTS_WITH, $pattern->getOp()); + + $pattern->setOp(PatternOperator::ENDS_WITH); + $this->assertEquals(PatternOperator::ENDS_WITH, $pattern->getOp()); + } + + /** + * Test toString + */ + public function testToString() + { + $pattern = new Pattern("city", PatternOperator::LIKE, "%ergam%"); + $this->assertEquals("city like %ergam%", (string)$pattern); + } +} diff --git a/tests/ApiFilter/FilterFactoryTest.php b/tests/ApiFilter/FilterFactoryTest.php index 46bf941..e776a19 100644 --- a/tests/ApiFilter/FilterFactoryTest.php +++ b/tests/ApiFilter/FilterFactoryTest.php @@ -10,7 +10,9 @@ use FattureInCloud\ApiFilter\Filter\EmptyField; use FattureInCloud\ApiFilter\Filter\FilledField; use FattureInCloud\ApiFilter\Filter\Filter; -use FattureInCloud\ApiFilter\Filter\Operator; +use FattureInCloud\ApiFilter\Filter\ComparisonOperator; +use FattureInCloud\ApiFilter\Filter\Pattern; +use FattureInCloud\ApiFilter\Filter\PatternOperator; use PHPUnit\Framework\TestCase; use Antlr\Antlr4\Runtime\Error\Exceptions\ParseCancellationException; @@ -53,7 +55,7 @@ public static function tearDownAfterClass(): void public function testEqualToOp() { $filter = $this->factory->initFilter("name = true"); - $expected = new Filter(new Comparison("name", Operator::EQ, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::EQ, true)); $this->assertEquals($expected, $filter); } @@ -63,7 +65,7 @@ public function testEqualToOp() public function testGreaterThanOp() { $filter = $this->factory->initFilter("name > true"); - $expected = new Filter(new Comparison("name", Operator::GT, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::GT, true)); $this->assertEquals($expected, $filter); } @@ -73,7 +75,7 @@ public function testGreaterThanOp() public function testGreaterThanOrEqualToOp() { $filter = $this->factory->initFilter("name >= true"); - $expected = new Filter(new Comparison("name", Operator::GTE, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::GTE, true)); $this->assertEquals($expected, $filter); } @@ -83,7 +85,7 @@ public function testGreaterThanOrEqualToOp() public function testLowerThanOp() { $filter = $this->factory->initFilter("name < true"); - $expected = new Filter(new Comparison("name", Operator::LT, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::LT, true)); $this->assertEquals($expected, $filter); } @@ -93,7 +95,7 @@ public function testLowerThanOp() public function testLowerThanOrEqualToOp() { $filter = $this->factory->initFilter("name <= true"); - $expected = new Filter(new Comparison("name", Operator::LTE, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::LTE, true)); $this->assertEquals($expected, $filter); } @@ -103,7 +105,7 @@ public function testLowerThanOrEqualToOp() public function testNotEqualToOp() { $filter = $this->factory->initFilter("name <> true"); - $expected = new Filter(new Comparison("name", Operator::NEQ, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::NEQ, true)); $this->assertEquals($expected, $filter); } @@ -113,7 +115,7 @@ public function testNotEqualToOp() public function testNotEqualToOpWithExclanation() { $filter = $this->factory->initFilter("name != true"); - $expected = new Filter(new Comparison("name", Operator::NEQ, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::NEQ, true)); $this->assertEquals($expected, $filter); } @@ -122,38 +124,139 @@ public function testNotEqualToOpWithExclanation() */ public function testLikeOp() { - $filter1 = $this->factory->initFilter("name like true"); - $expected1 = new Filter(new Comparison("name", Operator::LIKE, true)); + $filter1 = $this->factory->initFilter("name like '%ergam%'"); + $expected1 = new Filter(new Pattern("name", PatternOperator::LIKE, "%ergam%")); $this->assertEquals($expected1, $filter1); - $filter2 = $this->factory->initFilter("name LIKE true"); - $expected2 = new Filter(new Comparison("name", Operator::LIKE, true)); + $filter2 = $this->factory->initFilter("name LIKE '%ergam%'"); + $expected2 = new Filter(new Pattern("name", PatternOperator::LIKE, "%ergam%")); $this->assertEquals($expected2, $filter2); } + /** + * Test 'contains' operator + */ + public function testContainsOp() + { + $filter1 = $this->factory->initFilter("name contains 'ergam'"); + $expected1 = new Filter(new Pattern("name", PatternOperator::CONTAINS, "ergam")); + $this->assertEquals($expected1, $filter1); + + $filter2 = $this->factory->initFilter("name CONTAINS 'ergam'"); + $expected2 = new Filter(new Pattern("name", PatternOperator::CONTAINS, "ergam")); + $this->assertEquals($expected2, $filter2); + } + + /** + * Test 'starts with' operator + */ + public function testStartsWithOp() + { + $filter1 = $this->factory->initFilter("name starts with 'Mariano'"); + $expected1 = new Filter(new Pattern("name", PatternOperator::STARTS_WITH, "Mariano")); + $this->assertEquals($expected1, $filter1); + + $filter2 = $this->factory->initFilter("name STARTS WITH 'Mariano'"); + $expected2 = new Filter(new Pattern("name", PatternOperator::STARTS_WITH, "Mariano")); + $this->assertEquals($expected2, $filter2); + + $filter3 = $this->factory->initFilter("name starts WITH 'Mariano'"); + $expected3 = new Filter(new Pattern("name", PatternOperator::STARTS_WITH, "Mariano")); + $this->assertEquals($expected3, $filter3); + + $filter4 = $this->factory->initFilter("name STARTS with 'Mariano'"); + $expected4 = new Filter(new Pattern("name", PatternOperator::STARTS_WITH, "Mariano")); + $this->assertEquals($expected4, $filter4); + + $filter5 = $this->factory->initFilter("name startswith 'Mariano'"); + $expected5 = new Filter(new Pattern("name", PatternOperator::STARTS_WITH, "Mariano")); + $this->assertEquals($expected5, $filter5); + + $filter6 = $this->factory->initFilter("name STARTSWITH 'Mariano'"); + $expected6 = new Filter(new Pattern("name", PatternOperator::STARTS_WITH, "Mariano")); + $this->assertEquals($expected6, $filter6); + } + + /** + * Test 'ends with' operator + */ + public function testEndsWithOp() + { + $filter1 = $this->factory->initFilter("name ends with 'al Brembo'"); + $expected1 = new Filter(new Pattern("name", PatternOperator::ENDS_WITH, "al Brembo")); + $this->assertEquals($expected1, $filter1); + + $filter2 = $this->factory->initFilter("name ENDS WITH 'al Brembo'"); + $expected2 = new Filter(new Pattern("name", PatternOperator::ENDS_WITH, "al Brembo")); + $this->assertEquals($expected2, $filter2); + + $filter3 = $this->factory->initFilter("name ends WITH 'al Brembo'"); + $expected3 = new Filter(new Pattern("name", PatternOperator::ENDS_WITH, "al Brembo")); + $this->assertEquals($expected3, $filter3); + + $filter4 = $this->factory->initFilter("name ENDS with 'al Brembo'"); + $expected4 = new Filter(new Pattern("name", PatternOperator::ENDS_WITH, "al Brembo")); + $this->assertEquals($expected4, $filter4); + + $filter5 = $this->factory->initFilter("name endswith 'al Brembo'"); + $expected5 = new Filter(new Pattern("name", PatternOperator::ENDS_WITH, "al Brembo")); + $this->assertEquals($expected5, $filter5); + + $filter6 = $this->factory->initFilter("name ENDSWITH 'al Brembo'"); + $expected6 = new Filter(new Pattern("name", PatternOperator::ENDS_WITH, "al Brembo")); + $this->assertEquals($expected6, $filter6); + } + + /** + * Test no starts operator + */ + public function testNoStartsOperator() + { + $this->expectException(ParseCancellationException::class); + $this->factory->initFilter("name starts 'Mariano'"); + } + + /** + * Test no ends operator + */ + public function testNoEndsOperator() + { + $this->expectException(ParseCancellationException::class); + $this->factory->initFilter("name ends 'Mariano'"); + } + + /** + * Test no with operator + */ + public function testNoWithOperator() + { + $this->expectException(ParseCancellationException::class); + $this->factory->initFilter("name with 'Mariano'"); + } + /** * Test field name */ public function testFieldName() { $filter1 = $this->factory->initFilter("name = true"); - $expected1 = new Filter(new Comparison("name", Operator::EQ, true)); + $expected1 = new Filter(new Comparison("name", ComparisonOperator::EQ, true)); $this->assertEquals($expected1, $filter1); $filter2 = $this->factory->initFilter("field_name = true"); - $expected2 = new Filter(new Comparison("field_name", Operator::EQ, true)); + $expected2 = new Filter(new Comparison("field_name", ComparisonOperator::EQ, true)); $this->assertEquals($expected2, $filter2); $filter3 = $this->factory->initFilter("person.age = true"); - $expected3 = new Filter(new Comparison("person.age", Operator::EQ, true)); + $expected3 = new Filter(new Comparison("person.age", ComparisonOperator::EQ, true)); $this->assertEquals($expected3, $filter3); $filter4 = $this->factory->initFilter("person.last_name = true"); - $expected4 = new Filter(new Comparison("person.last_name", Operator::EQ, true)); + $expected4 = new Filter(new Comparison("person.last_name", ComparisonOperator::EQ, true)); $this->assertEquals($expected4, $filter4); $filter5 = $this->factory->initFilter("person_name.first = true"); - $expected5 = new Filter(new Comparison("person_name.first", Operator::EQ, true)); + $expected5 = new Filter(new Comparison("person_name.first", ComparisonOperator::EQ, true)); $this->assertEquals($expected5, $filter5); } @@ -163,7 +266,7 @@ public function testFieldName() public function testOneLenField() { $filter = $this->factory->initFilter("x = 1"); - $expected = new Filter(new Comparison("x", Operator::EQ, 1)); + $expected = new Filter(new Comparison("x", ComparisonOperator::EQ, 1)); $this->assertEquals($expected, $filter); } @@ -173,7 +276,7 @@ public function testOneLenField() public function testTwoLenField() { $filter = $this->factory->initFilter("id = 2"); - $expected = new Filter(new Comparison("id", Operator::EQ, 2)); + $expected = new Filter(new Comparison("id", ComparisonOperator::EQ, 2)); $this->assertEquals($expected, $filter); } @@ -184,7 +287,7 @@ public function testLongField() { $fieldName = "ilkobranoneunserpentemaunpensierofrequentechediventaindecentequandovedotequandovedotequandovedotequandovedote"; $filter = $this->factory->initFilter($fieldName . " = 109"); - $expected = new Filter(new Comparison($fieldName, Operator::EQ, 109)); + $expected = new Filter(new Comparison($fieldName, ComparisonOperator::EQ, 109)); $this->assertEquals($expected, $filter); } @@ -221,11 +324,11 @@ public function testMissingFieldNameFollowedByLike() public function testBooleanValue() { $filter1 = $this->factory->initFilter("name = true"); - $expected1 = new Filter(new Comparison("name", Operator::EQ, true)); + $expected1 = new Filter(new Comparison("name", ComparisonOperator::EQ, true)); $this->assertEquals($expected1, $filter1); $filter2 = $this->factory->initFilter("name = false"); - $expected2 = new Filter(new Comparison("name", Operator::EQ, false)); + $expected2 = new Filter(new Comparison("name", ComparisonOperator::EQ, false)); $this->assertEquals($expected2, $filter2); } @@ -235,11 +338,11 @@ public function testBooleanValue() public function testNumberValue() { $filter1 = $this->factory->initFilter("name = 5000"); - $expected1 = new Filter(new Comparison("name", Operator::EQ, 5000)); + $expected1 = new Filter(new Comparison("name", ComparisonOperator::EQ, 5000)); $this->assertEquals($expected1, $filter1); $filter2 = $this->factory->initFilter("name = 5123.2555"); - $expected2 = new Filter(new Comparison("name", Operator::EQ, 5123.2555)); + $expected2 = new Filter(new Comparison("name", ComparisonOperator::EQ, 5123.2555)); $this->assertEquals($expected2, $filter2); } @@ -276,51 +379,51 @@ public function testNoMultipleDots() public function testStringValue() { $filter1 = $this->factory->initFilter("name = 'Guillaume'"); - $expected1 = new Filter(new Comparison("name", Operator::EQ, "Guillaume")); + $expected1 = new Filter(new Comparison("name", ComparisonOperator::EQ, "Guillaume")); $this->assertEquals($expected1, $filter1); $filter2 = $this->factory->initFilter("name = '5123.2555'"); - $expected2 = new Filter(new Comparison("name", Operator::EQ, '5123.2555')); + $expected2 = new Filter(new Comparison("name", ComparisonOperator::EQ, '5123.2555')); $this->assertEquals($expected2, $filter2); $filter3 = $this->factory->initFilter("name = ' '"); - $expected3 = new Filter(new Comparison("name", Operator::EQ, ' ')); + $expected3 = new Filter(new Comparison("name", ComparisonOperator::EQ, ' ')); $this->assertEquals($expected3, $filter3); $filter4 = $this->factory->initFilter("name = '\"'"); - $expected4 = new Filter(new Comparison("name", Operator::EQ, '"')); + $expected4 = new Filter(new Comparison("name", ComparisonOperator::EQ, '"')); $this->assertEquals($expected4, $filter4); $filter5 = $this->factory->initFilter("name = 'true'"); - $expected5 = new Filter(new Comparison("name", Operator::EQ, 'true')); + $expected5 = new Filter(new Comparison("name", ComparisonOperator::EQ, 'true')); $this->assertEquals($expected5, $filter5); $filter6 = $this->factory->initFilter("name = 'false'"); - $expected6 = new Filter(new Comparison("name", Operator::EQ, 'false')); + $expected6 = new Filter(new Comparison("name", ComparisonOperator::EQ, 'false')); $this->assertEquals($expected6, $filter6); $filter7 = $this->factory->initFilter("name = 'null'"); - $expected7 = new Filter(new Comparison("name", Operator::EQ, 'null')); + $expected7 = new Filter(new Comparison("name", ComparisonOperator::EQ, 'null')); $this->assertEquals($expected7, $filter7); $filter8 = $this->factory->initFilter("name = 'NULL'"); - $expected8 = new Filter(new Comparison("name", Operator::EQ, 'NULL')); + $expected8 = new Filter(new Comparison("name", ComparisonOperator::EQ, 'NULL')); $this->assertEquals($expected8, $filter8); $filter9 = $this->factory->initFilter("name != 'null'"); - $expected9 = new Filter(new Comparison("name", Operator::NEQ, 'null')); + $expected9 = new Filter(new Comparison("name", ComparisonOperator::NEQ, 'null')); $this->assertEquals($expected9, $filter9); $filter10 = $this->factory->initFilter("name != 'NULL'"); - $expected10 = new Filter(new Comparison("name", Operator::NEQ, 'NULL')); + $expected10 = new Filter(new Comparison("name", ComparisonOperator::NEQ, 'NULL')); $this->assertEquals($expected10, $filter10); $filter11 = $this->factory->initFilter("name <> 'null'"); - $expected11 = new Filter(new Comparison("name", Operator::NEQ, 'null')); + $expected11 = new Filter(new Comparison("name", ComparisonOperator::NEQ, 'null')); $this->assertEquals($expected11, $filter11); $filter12 = $this->factory->initFilter("name <> 'NULL'"); - $expected12 = new Filter(new Comparison("name", Operator::NEQ, 'NULL')); + $expected12 = new Filter(new Comparison("name", ComparisonOperator::NEQ, 'NULL')); $this->assertEquals($expected12, $filter12); } @@ -489,13 +592,22 @@ public function testNoStringValueWithIsNot() $this->factory->initFilter("name IS NOT 'NULL'"); } + /** + * Test no NOT operator + */ + public function testNoNotOperator() + { + $this->expectException(ParseCancellationException::class); + $this->factory->initFilter("name NOT NULL"); + } + /** * Test parenthesis */ public function testParenthesis() { $filter = $this->factory->initFilter("(name = true)"); - $expected = new Filter(new Comparison("name", Operator::EQ, true)); + $expected = new Filter(new Comparison("name", ComparisonOperator::EQ, true)); $this->assertEquals($expected, $filter); } @@ -525,8 +637,8 @@ public function testConjunction() $filter1 = $this->factory->initFilter("name = 'Guillaume' and company <> 'FIC'"); $expected1 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected1, $filter1); @@ -534,8 +646,8 @@ public function testConjunction() $filter2 = $this->factory->initFilter("name = 'Guillaume' AND company <> 'FIC'"); $expected2 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected2, $filter2); @@ -543,8 +655,8 @@ public function testConjunction() $filter3 = $this->factory->initFilter("(name = 'Guillaume') and company <> 'FIC'"); $expected3 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected3, $filter3); @@ -552,8 +664,8 @@ public function testConjunction() $filter4 = $this->factory->initFilter("name = 'Guillaume' and (company <> 'FIC')"); $expected4 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected4, $filter4); @@ -561,8 +673,8 @@ public function testConjunction() $filter5 = $this->factory->initFilter("(name = 'Guillaume' and company <> 'FIC')"); $expected5 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected5, $filter5); @@ -597,8 +709,8 @@ public function testDisjunction() $filter1 = $this->factory->initFilter("name = 'Guillaume' or company <> 'FIC'"); $expected1 = new Filter( new Disjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected1, $filter1); @@ -606,8 +718,8 @@ public function testDisjunction() $filter2 = $this->factory->initFilter("name = 'Guillaume' OR company <> 'FIC'"); $expected2 = new Filter( new Disjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected2, $filter2); @@ -615,8 +727,8 @@ public function testDisjunction() $filter3 = $this->factory->initFilter("(name = 'Guillaume') or company <> 'FIC'"); $expected3 = new Filter( new Disjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected3, $filter3); @@ -624,8 +736,8 @@ public function testDisjunction() $filter4 = $this->factory->initFilter("name = 'Guillaume' or (company <> 'FIC')"); $expected4 = new Filter( new Disjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected4, $filter4); @@ -633,8 +745,8 @@ public function testDisjunction() $filter5 = $this->factory->initFilter("(name = 'Guillaume' or company <> 'FIC')"); $expected5 = new Filter( new Disjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected5, $filter5); @@ -669,10 +781,10 @@ public function testComplexExpressions() $filter1 = $this->factory->initFilter("name = 'Guillaume' and (city = 'Bergamo' or company <> 'FIC')"); $expected1 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), new Disjunction( - new Comparison("city", Operator::EQ, "Bergamo"), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("city", ComparisonOperator::EQ, "Bergamo"), + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ) ); @@ -681,9 +793,9 @@ public function testComplexExpressions() $filter2 = $this->factory->initFilter("name = 'Guillaume' and (city = 'Bergamo' or company is not null)"); $expected2 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), new Disjunction( - new Comparison("city", Operator::EQ, "Bergamo"), + new Comparison("city", ComparisonOperator::EQ, "Bergamo"), new FilledField("company") ) ) @@ -694,10 +806,10 @@ public function testComplexExpressions() $expected3 = new Filter( new Disjunction( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::EQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::EQ, "Bergamo") ), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected3, $filter3); @@ -706,10 +818,10 @@ public function testComplexExpressions() $expected4 = new Filter( new Disjunction( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::EQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::EQ, "Bergamo") ), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected4, $filter4); @@ -718,8 +830,8 @@ public function testComplexExpressions() $expected5 = new Filter( new Disjunction( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::EQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::EQ, "Bergamo") ), new EmptyField("company") ) @@ -730,10 +842,10 @@ public function testComplexExpressions() $expected6 = new Filter( new Conjunction( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::EQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::EQ, "Bergamo") ), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected6, $filter6); @@ -742,13 +854,34 @@ public function testComplexExpressions() $expected7 = new Filter( new Disjunction( new Disjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::EQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::EQ, "Bergamo") ), - new Comparison("company", Operator::NEQ, "FIC") + new Comparison("company", ComparisonOperator::NEQ, "FIC") ) ); $this->assertEquals($expected7, $filter7); + + $filter8 = $this->factory->initFilter("city = 'Bergamo' and (age < 30 or (dev = true and (name = 'Giorgio' and surname is not null) or employer starts with 'Fatture'))"); + $expected8 = new Filter( + new Conjunction( + new Comparison("city", ComparisonOperator::EQ, "Bergamo"), + new Disjunction( + new Comparison("age", ComparisonOperator::LT, 30), + new Disjunction( + new Conjunction( + new Comparison("dev", ComparisonOperator::EQ, true), + new Conjunction( + new Comparison("name", ComparisonOperator::EQ, "Giorgio"), + new FilledField("surname") + ) + ), + new Pattern("employer", PatternOperator::STARTS_WITH, "Fatture") + ) + ) + ) + ); + $this->assertEquals($expected8, $filter8); } /** @@ -795,8 +928,8 @@ public function testFormatted() $filter = $this->factory->initFilter("name = 'Guillaume'\nand\ncity <> 'Bergamo'"); $expected = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::NEQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::NEQ, "Bergamo") ) ); $this->assertEquals($expected, $filter); @@ -811,8 +944,8 @@ public function testSkippingSpaces() $filter1 = $this->factory->initFilter($str); $expected1 = new Filter( new Conjunction( - new Comparison("name", Operator::EQ, "Guillaume"), - new Comparison("city", Operator::NEQ, "Bergamo") + new Comparison("name", ComparisonOperator::EQ, "Guillaume"), + new Comparison("city", ComparisonOperator::NEQ, "Bergamo") ) ); $this->assertEquals($expected1, $filter1);