소스 검색

Painless: =~ and ==~ operators

Adds support for the find operator (=~) and the match operator (==~)
to painless's regexes. Also whitelists most of the Matcher class and
documents regex support in painless.

The find operator (=~) returns a boolean that is the result of building
a matcher on the lhs with the Pattern on the RHS and calling `find` on
it. Use it like this:

```
if (ctx._source.last =~ /b/)
```

The match operator (==~) returns boolean like find but instead of calling
`find` on the Matcher it calls `matches`.

```
if (ctx._source.last ==~ /[^aeiou].*[aeiou]/)
```

Finally, if you want the actual matcher you do:

```
Matcher m = /[aeiou]/.matcher(ctx._source.last)
```
Nik Everett 9 년 전
부모
커밋
8d3ef742db
17개의 변경된 파일746개의 추가작업 그리고 503개의 파일을 삭제
  1. 75 0
      docs/reference/modules/scripting/painless.asciidoc
  2. 2 0
      modules/lang-painless/src/main/antlr/PainlessLexer.g4
  3. 48 44
      modules/lang-painless/src/main/antlr/PainlessLexer.tokens
  4. 1 0
      modules/lang-painless/src/main/antlr/PainlessParser.g4
  5. 48 44
      modules/lang-painless/src/main/antlr/PainlessParser.tokens
  6. 3 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java
  7. 0 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java
  8. 2 0
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Operation.java
  9. 5 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java
  10. 208 202
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java
  11. 195 173
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java
  12. 4 0
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java
  13. 32 0
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java
  14. 4 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LRegex.java
  15. 26 0
      modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.util.regex.txt
  16. 93 15
      modules/lang-painless/src/test/java/org/elasticsearch/painless/RegexTests.java
  17. 0 16
      modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java

+ 75 - 0
docs/reference/modules/scripting/painless.asciidoc

@@ -33,6 +33,8 @@ to `painless`.
 
 * Shortcuts for list, map access using the dot `.` operator
 
+* Native support for regular expressions with `/pattern/`, `=~`, and `==~`
+
 
 [[painless-examples]]
 [float]
@@ -199,6 +201,79 @@ POST hockey/player/1/_update
 ----------------------------------------------------------------
 // CONSOLE
 
+[float]
+=== Regular expressions
+
+Painless's native support for regular expressions has syntax constructs:
+
+* `/pattern/`: Pattern literals create patterns. This is the only way to create
+a pattern in painless.
+* `=~`: The find operator return a `boolean`, `true` if a subsequence of the
+text matches, `false` otherwise.
+* `==~`: The match operator returns a `boolean`, `true` if the text matches,
+`false` if it doesn't.
+
+Using the find operator (`=~`) you can update all hockey players with "b" in
+their last name:
+
+[source,js]
+----------------------------------------------------------------
+POST hockey/player/_update_by_query
+{
+  "script": {
+    "lang": "painless",
+    "inline": "if (ctx._source.last =~ /b/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}"
+  }
+}
+----------------------------------------------------------------
+// CONSOLE
+
+Using the match operator (`==~`) you can update all the hockey players who's
+names start with a consonant and end with a vowel:
+
+[source,js]
+----------------------------------------------------------------
+POST hockey/player/_update_by_query
+{
+  "script": {
+    "lang": "painless",
+    "inline": "if (ctx._source.last ==~ /[^aeiou].*[aeiou]/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}"
+  }
+}
+----------------------------------------------------------------
+// CONSOLE
+
+Or you can use the `Pattern.matcher` directory to get a `Matcher` instance and
+remove all of the vowels in all of their names:
+
+[source,js]
+----------------------------------------------------------------
+POST hockey/player/_update_by_query
+{
+  "script": {
+    "lang": "painless",
+    "inline": "ctx._source.last = /[aeiou]/.matcher(ctx._source.last).replaceAll('')"
+  }
+}
+----------------------------------------------------------------
+// CONSOLE
+
+
+Note: all of the `_update_by_query` examples above could really do with a
+`query` to limit the data that they pull back. While you *could* use a
+<<query-dsl-script-query>> it wouldn't be as efficient as using any other query
+because script queries aren't able to use the inverted index to limit the
+documents that they have to check.
+
+The pattern syntax is just
+http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java regular expressions].
+We intentionally don't allow scripts to call `Pattern.compile` to get a new
+pattern on the fly because building a `Pattern` is (comparatively) slow.
+Pattern literals (`/apattern/`) have fancy constant extraction so no matter
+where they show up in the painless script they are built only when the script
+is first used. It is fairly similar to how `String` literals work in Java.
+
+
 [[painless-api]]
 [float]
 == Painless API

+ 2 - 0
modules/lang-painless/src/main/antlr/PainlessLexer.g4

@@ -80,6 +80,8 @@ COND:    '?';
 COLON:   ':';
 REF:     '::';
 ARROW:   '->';
+FIND:    '=~';
+MATCH:   '==~';
 INCR:    '++';
 DECR:    '--';
 

+ 48 - 44
modules/lang-painless/src/main/antlr/PainlessLexer.tokens

@@ -49,33 +49,35 @@ COND=48
 COLON=49
 REF=50
 ARROW=51
-INCR=52
-DECR=53
-ASSIGN=54
-AADD=55
-ASUB=56
-AMUL=57
-ADIV=58
-AREM=59
-AAND=60
-AXOR=61
-AOR=62
-ALSH=63
-ARSH=64
-AUSH=65
-OCTAL=66
-HEX=67
-INTEGER=68
-DECIMAL=69
-STRING=70
-REGEX=71
-TRUE=72
-FALSE=73
-NULL=74
-TYPE=75
-ID=76
-DOTINTEGER=77
-DOTID=78
+FIND=52
+MATCH=53
+INCR=54
+DECR=55
+ASSIGN=56
+AADD=57
+ASUB=58
+AMUL=59
+ADIV=60
+AREM=61
+AAND=62
+AXOR=63
+AOR=64
+ALSH=65
+ARSH=66
+AUSH=67
+OCTAL=68
+HEX=69
+INTEGER=70
+DECIMAL=71
+STRING=72
+REGEX=73
+TRUE=74
+FALSE=75
+NULL=76
+TYPE=77
+ID=78
+DOTINTEGER=79
+DOTID=80
 '{'=3
 '}'=4
 '['=5
@@ -125,20 +127,22 @@ DOTID=78
 ':'=49
 '::'=50
 '->'=51
-'++'=52
-'--'=53
-'='=54
-'+='=55
-'-='=56
-'*='=57
-'/='=58
-'%='=59
-'&='=60
-'^='=61
-'|='=62
-'<<='=63
-'>>='=64
-'>>>='=65
-'true'=72
-'false'=73
-'null'=74
+'=~'=52
+'==~'=53
+'++'=54
+'--'=55
+'='=56
+'+='=57
+'-='=58
+'*='=59
+'/='=60
+'%='=61
+'&='=62
+'^='=63
+'|='=64
+'<<='=65
+'>>='=66
+'>>>='=67
+'true'=74
+'false'=75
+'null'=76

+ 1 - 0
modules/lang-painless/src/main/antlr/PainlessParser.g4

@@ -113,6 +113,7 @@ expression returns [boolean s = true]
     :               u = unary[false]                                       { $s = $u.s; }           # single
     |               expression ( MUL | DIV | REM ) expression              { $s = false; }          # binary
     |               expression ( ADD | SUB ) expression                    { $s = false; }          # binary
+    |               expression ( FIND | MATCH ) expression                 { $s = false; }          # binary
     |               expression ( LSH | RSH | USH ) expression              { $s = false; }          # binary
     |               expression ( LT | LTE | GT | GTE ) expression          { $s = false; }          # comp
     |               expression ( EQ | EQR | NE | NER ) expression          { $s = false; }          # comp

+ 48 - 44
modules/lang-painless/src/main/antlr/PainlessParser.tokens

@@ -49,33 +49,35 @@ COND=48
 COLON=49
 REF=50
 ARROW=51
-INCR=52
-DECR=53
-ASSIGN=54
-AADD=55
-ASUB=56
-AMUL=57
-ADIV=58
-AREM=59
-AAND=60
-AXOR=61
-AOR=62
-ALSH=63
-ARSH=64
-AUSH=65
-OCTAL=66
-HEX=67
-INTEGER=68
-DECIMAL=69
-STRING=70
-REGEX=71
-TRUE=72
-FALSE=73
-NULL=74
-TYPE=75
-ID=76
-DOTINTEGER=77
-DOTID=78
+FIND=52
+MATCH=53
+INCR=54
+DECR=55
+ASSIGN=56
+AADD=57
+ASUB=58
+AMUL=59
+ADIV=60
+AREM=61
+AAND=62
+AXOR=63
+AOR=64
+ALSH=65
+ARSH=66
+AUSH=67
+OCTAL=68
+HEX=69
+INTEGER=70
+DECIMAL=71
+STRING=72
+REGEX=73
+TRUE=74
+FALSE=75
+NULL=76
+TYPE=77
+ID=78
+DOTINTEGER=79
+DOTID=80
 '{'=3
 '}'=4
 '['=5
@@ -125,20 +127,22 @@ DOTID=78
 ':'=49
 '::'=50
 '->'=51
-'++'=52
-'--'=53
-'='=54
-'+='=55
-'-='=56
-'*='=57
-'/='=58
-'%='=59
-'&='=60
-'^='=61
-'|='=62
-'<<='=63
-'>>='=64
-'>>>='=65
-'true'=72
-'false'=73
-'null'=74
+'=~'=52
+'==~'=53
+'++'=54
+'--'=55
+'='=56
+'+='=57
+'-='=58
+'*='=59
+'/='=60
+'%='=61
+'&='=62
+'^='=63
+'|='=64
+'<<='=65
+'>>='=66
+'>>>='=67
+'true'=74
+'false'=75
+'null'=76

+ 3 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java

@@ -87,6 +87,8 @@ public final class Definition {
     public static final Type DEF_TYPE = getType("def");
     public static final Type STRING_TYPE = getType("String");
     public static final Type EXCEPTION_TYPE = getType("Exception");
+    public static final Type PATTERN_TYPE = getType("Pattern");
+    public static final Type MATCHER_TYPE = getType("Matcher");
 
     public enum Sort {
         VOID(       void.class      , 0 , true  , false , false , false ),
@@ -772,7 +774,7 @@ public final class Definition {
 
         if (owner.staticMethods.containsKey(methodKey) || owner.methods.containsKey(methodKey)) {
             throw new IllegalArgumentException(
-                "Duplicate  method signature [" + methodKey + "] found within the struct [" + owner.name + "].");
+                "Duplicate method signature [" + methodKey + "] found within the struct [" + owner.name + "].");
         }
 
         final Class<?>[] classes = new Class<?>[args.length];

+ 0 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java

@@ -386,5 +386,4 @@ public final class MethodWriter extends GeneratorAdapter {
     public void visitEnd() {
         throw new AssertionError("Should never call this method on MethodWriter, use endMethod() instead");
     }
-
 }

+ 2 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/Operation.java

@@ -32,6 +32,8 @@ public enum Operation {
     REM     ( "%"   ),
     ADD     ( "+"   ),
     SUB     ( "-"   ),
+    FIND    ( "=~"  ),
+    MATCH   ( "==~" ),
     LSH     ( "<<"  ),
     RSH     ( ">>"  ),
     USH     ( ">>>" ),

+ 5 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java

@@ -35,6 +35,7 @@ import java.util.BitSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
@@ -73,12 +74,15 @@ public final class WriterConstants {
     public final static Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
 
     /**
-     * A Method instance for {@linkplain Pattern#compile}. This isn't looked up from Definition because we intentionally don't add it there
+     * A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
      * so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes
      * because it can do it statically. This is both faster and prevents the script from doing something super slow like building a regex
      * per time it is run.
      */
     public final static Method PATTERN_COMPILE = getAsmMethod(Pattern.class, "compile", String.class);
+    public final static Method PATTERN_MATCHER = getAsmMethod(Matcher.class, "matcher", CharSequence.class);
+    public final static Method MATCHER_MATCHES = getAsmMethod(boolean.class, "matches");
+    public final static Method MATCHER_FIND = getAsmMethod(boolean.class, "find");
 
     /** dynamic callsite bootstrap signature */
     public final static MethodType DEF_BOOTSTRAP_TYPE =

+ 208 - 202
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java

@@ -26,10 +26,10 @@ class PainlessLexer extends Lexer {
     BWNOT=26, MUL=27, DIV=28, REM=29, ADD=30, SUB=31, LSH=32, RSH=33, USH=34, 
     LT=35, LTE=36, GT=37, GTE=38, EQ=39, EQR=40, NE=41, NER=42, BWAND=43, 
     XOR=44, BWOR=45, BOOLAND=46, BOOLOR=47, COND=48, COLON=49, REF=50, ARROW=51, 
-    INCR=52, DECR=53, ASSIGN=54, AADD=55, ASUB=56, AMUL=57, ADIV=58, AREM=59, 
-    AAND=60, AXOR=61, AOR=62, ALSH=63, ARSH=64, AUSH=65, OCTAL=66, HEX=67, 
-    INTEGER=68, DECIMAL=69, STRING=70, REGEX=71, TRUE=72, FALSE=73, NULL=74, 
-    TYPE=75, ID=76, DOTINTEGER=77, DOTID=78;
+    FIND=52, MATCH=53, INCR=54, DECR=55, ASSIGN=56, AADD=57, ASUB=58, AMUL=59, 
+    ADIV=60, AREM=61, AAND=62, AXOR=63, AOR=64, ALSH=65, ARSH=66, AUSH=67, 
+    OCTAL=68, HEX=69, INTEGER=70, DECIMAL=71, STRING=72, REGEX=73, TRUE=74, 
+    FALSE=75, NULL=76, TYPE=77, ID=78, DOTINTEGER=79, DOTID=80;
   public static final int AFTER_DOT = 1;
   public static String[] modeNames = {
     "DEFAULT_MODE", "AFTER_DOT"
@@ -41,10 +41,11 @@ class PainlessLexer extends Lexer {
     "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW", "THIS", "BOOLNOT", 
     "BWNOT", "MUL", "DIV", "REM", "ADD", "SUB", "LSH", "RSH", "USH", "LT", 
     "LTE", "GT", "GTE", "EQ", "EQR", "NE", "NER", "BWAND", "XOR", "BWOR", 
-    "BOOLAND", "BOOLOR", "COND", "COLON", "REF", "ARROW", "INCR", "DECR", 
-    "ASSIGN", "AADD", "ASUB", "AMUL", "ADIV", "AREM", "AAND", "AXOR", "AOR", 
-    "ALSH", "ARSH", "AUSH", "OCTAL", "HEX", "INTEGER", "DECIMAL", "STRING", 
-    "REGEX", "TRUE", "FALSE", "NULL", "TYPE", "ID", "DOTINTEGER", "DOTID"
+    "BOOLAND", "BOOLOR", "COND", "COLON", "REF", "ARROW", "FIND", "MATCH", 
+    "INCR", "DECR", "ASSIGN", "AADD", "ASUB", "AMUL", "ADIV", "AREM", "AAND", 
+    "AXOR", "AOR", "ALSH", "ARSH", "AUSH", "OCTAL", "HEX", "INTEGER", "DECIMAL", 
+    "STRING", "REGEX", "TRUE", "FALSE", "NULL", "TYPE", "ID", "DOTINTEGER", 
+    "DOTID"
   };
 
   private static final String[] _LITERAL_NAMES = {
@@ -53,9 +54,10 @@ class PainlessLexer extends Lexer {
     "'return'", "'new'", "'try'", "'catch'", "'throw'", "'this'", "'!'", "'~'", 
     "'*'", "'/'", "'%'", "'+'", "'-'", "'<<'", "'>>'", "'>>>'", "'<'", "'<='", 
     "'>'", "'>='", "'=='", "'==='", "'!='", "'!=='", "'&'", "'^'", "'|'", 
-    "'&&'", "'||'", "'?'", "':'", "'::'", "'->'", "'++'", "'--'", "'='", "'+='", 
-    "'-='", "'*='", "'/='", "'%='", "'&='", "'^='", "'|='", "'<<='", "'>>='", 
-    "'>>>='", null, null, null, null, null, null, "'true'", "'false'", "'null'"
+    "'&&'", "'||'", "'?'", "':'", "'::'", "'->'", "'=~'", "'==~'", "'++'", 
+    "'--'", "'='", "'+='", "'-='", "'*='", "'/='", "'%='", "'&='", "'^='", 
+    "'|='", "'<<='", "'>>='", "'>>>='", null, null, null, null, null, null, 
+    "'true'", "'false'", "'null'"
   };
   private static final String[] _SYMBOLIC_NAMES = {
     null, "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP", 
@@ -63,10 +65,11 @@ class PainlessLexer extends Lexer {
     "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW", "THIS", "BOOLNOT", 
     "BWNOT", "MUL", "DIV", "REM", "ADD", "SUB", "LSH", "RSH", "USH", "LT", 
     "LTE", "GT", "GTE", "EQ", "EQR", "NE", "NER", "BWAND", "XOR", "BWOR", 
-    "BOOLAND", "BOOLOR", "COND", "COLON", "REF", "ARROW", "INCR", "DECR", 
-    "ASSIGN", "AADD", "ASUB", "AMUL", "ADIV", "AREM", "AAND", "AXOR", "AOR", 
-    "ALSH", "ARSH", "AUSH", "OCTAL", "HEX", "INTEGER", "DECIMAL", "STRING", 
-    "REGEX", "TRUE", "FALSE", "NULL", "TYPE", "ID", "DOTINTEGER", "DOTID"
+    "BOOLAND", "BOOLOR", "COND", "COLON", "REF", "ARROW", "FIND", "MATCH", 
+    "INCR", "DECR", "ASSIGN", "AADD", "ASUB", "AMUL", "ADIV", "AREM", "AAND", 
+    "AXOR", "AOR", "ALSH", "ARSH", "AUSH", "OCTAL", "HEX", "INTEGER", "DECIMAL", 
+    "STRING", "REGEX", "TRUE", "FALSE", "NULL", "TYPE", "ID", "DOTINTEGER", 
+    "DOTID"
   };
   public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
 
@@ -127,9 +130,9 @@ class PainlessLexer extends Lexer {
     switch (ruleIndex) {
     case 27:
       return DIV_sempred((RuleContext)_localctx, predIndex);
-    case 70:
+    case 72:
       return REGEX_sempred((RuleContext)_localctx, predIndex);
-    case 74:
+    case 76:
       return TYPE_sempred((RuleContext)_localctx, predIndex);
     }
     return true;
@@ -157,7 +160,7 @@ class PainlessLexer extends Lexer {
   }
 
   public static final String _serializedATN =
-    "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2P\u0228\b\1\b\1\4"+
+    "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2R\u0233\b\1\b\1\4"+
     "\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n"+
     "\4\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22"+
     "\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31"+
@@ -166,190 +169,193 @@ class PainlessLexer extends Lexer {
     "+\4,\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64"+
     "\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t"+
     "=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4"+
-    "I\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\3\2\6\2\u00a2\n\2\r\2\16\2\u00a3"+
-    "\3\2\3\2\3\3\3\3\3\3\3\3\7\3\u00ac\n\3\f\3\16\3\u00af\13\3\3\3\3\3\3\3"+
-    "\3\3\3\3\7\3\u00b6\n\3\f\3\16\3\u00b9\13\3\3\3\3\3\5\3\u00bd\n\3\3\3\3"+
-    "\3\3\4\3\4\3\5\3\5\3\6\3\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3\n\3\n\3\13"+
-    "\3\13\3\f\3\f\3\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3\17\3\17\3\17\3\17"+
-    "\3\17\3\17\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22"+
-    "\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\24"+
-    "\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\27\3\27\3\27"+
-    "\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3\31"+
-    "\3\32\3\32\3\33\3\33\3\34\3\34\3\35\3\35\3\35\3\36\3\36\3\37\3\37\3 \3"+
-    " \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3#\3$\3$\3%\3%\3%\3&\3&\3\'\3\'\3\'\3"+
-    "(\3(\3(\3)\3)\3)\3)\3*\3*\3*\3+\3+\3+\3+\3,\3,\3-\3-\3.\3.\3/\3/\3/\3"+
-    "\60\3\60\3\60\3\61\3\61\3\62\3\62\3\63\3\63\3\63\3\64\3\64\3\64\3\65\3"+
-    "\65\3\65\3\66\3\66\3\66\3\67\3\67\38\38\38\39\39\39\3:\3:\3:\3;\3;\3;"+
-    "\3<\3<\3<\3=\3=\3=\3>\3>\3>\3?\3?\3?\3@\3@\3@\3@\3A\3A\3A\3A\3B\3B\3B"+
-    "\3B\3B\3C\3C\6C\u018f\nC\rC\16C\u0190\3C\5C\u0194\nC\3D\3D\3D\6D\u0199"+
-    "\nD\rD\16D\u019a\3D\5D\u019e\nD\3E\3E\3E\7E\u01a3\nE\fE\16E\u01a6\13E"+
-    "\5E\u01a8\nE\3E\5E\u01ab\nE\3F\3F\3F\7F\u01b0\nF\fF\16F\u01b3\13F\5F\u01b5"+
-    "\nF\3F\3F\6F\u01b9\nF\rF\16F\u01ba\5F\u01bd\nF\3F\3F\5F\u01c1\nF\3F\6"+
-    "F\u01c4\nF\rF\16F\u01c5\5F\u01c8\nF\3F\5F\u01cb\nF\3G\3G\3G\3G\3G\3G\7"+
-    "G\u01d3\nG\fG\16G\u01d6\13G\3G\3G\3G\3G\3G\3G\3G\7G\u01df\nG\fG\16G\u01e2"+
-    "\13G\3G\5G\u01e5\nG\3H\3H\3H\3H\6H\u01eb\nH\rH\16H\u01ec\3H\3H\3H\3I\3"+
-    "I\3I\3I\3I\3J\3J\3J\3J\3J\3J\3K\3K\3K\3K\3K\3L\3L\3L\3L\7L\u0206\nL\f"+
-    "L\16L\u0209\13L\3L\3L\3M\3M\7M\u020f\nM\fM\16M\u0212\13M\3N\3N\3N\7N\u0217"+
-    "\nN\fN\16N\u021a\13N\5N\u021c\nN\3N\3N\3O\3O\7O\u0222\nO\fO\16O\u0225"+
-    "\13O\3O\3O\6\u00ad\u00b7\u01d4\u01e0\2P\4\3\6\4\b\5\n\6\f\7\16\b\20\t"+
-    "\22\n\24\13\26\f\30\r\32\16\34\17\36\20 \21\"\22$\23&\24(\25*\26,\27."+
-    "\30\60\31\62\32\64\33\66\348\35:\36<\37> @!B\"D#F$H%J&L\'N(P)R*T+V,X-"+
-    "Z.\\/^\60`\61b\62d\63f\64h\65j\66l\67n8p9r:t;v<x=z>|?~@\u0080A\u0082B"+
-    "\u0084C\u0086D\u0088E\u008aF\u008cG\u008eH\u0090I\u0092J\u0094K\u0096"+
-    "L\u0098M\u009aN\u009cO\u009eP\4\2\3\23\5\2\13\f\17\17\"\"\4\2\f\f\17\17"+
-    "\3\2\629\4\2NNnn\4\2ZZzz\5\2\62;CHch\3\2\63;\3\2\62;\b\2FFHHNNffhhnn\4"+
-    "\2GGgg\4\2--//\4\2HHhh\4\2$$^^\4\2\f\f\61\61\3\2\f\f\5\2C\\aac|\6\2\62"+
-    ";C\\aac|\u0247\2\4\3\2\2\2\2\6\3\2\2\2\2\b\3\2\2\2\2\n\3\2\2\2\2\f\3\2"+
-    "\2\2\2\16\3\2\2\2\2\20\3\2\2\2\2\22\3\2\2\2\2\24\3\2\2\2\2\26\3\2\2\2"+
-    "\2\30\3\2\2\2\2\32\3\2\2\2\2\34\3\2\2\2\2\36\3\2\2\2\2 \3\2\2\2\2\"\3"+
-    "\2\2\2\2$\3\2\2\2\2&\3\2\2\2\2(\3\2\2\2\2*\3\2\2\2\2,\3\2\2\2\2.\3\2\2"+
-    "\2\2\60\3\2\2\2\2\62\3\2\2\2\2\64\3\2\2\2\2\66\3\2\2\2\28\3\2\2\2\2:\3"+
-    "\2\2\2\2<\3\2\2\2\2>\3\2\2\2\2@\3\2\2\2\2B\3\2\2\2\2D\3\2\2\2\2F\3\2\2"+
-    "\2\2H\3\2\2\2\2J\3\2\2\2\2L\3\2\2\2\2N\3\2\2\2\2P\3\2\2\2\2R\3\2\2\2\2"+
-    "T\3\2\2\2\2V\3\2\2\2\2X\3\2\2\2\2Z\3\2\2\2\2\\\3\2\2\2\2^\3\2\2\2\2`\3"+
-    "\2\2\2\2b\3\2\2\2\2d\3\2\2\2\2f\3\2\2\2\2h\3\2\2\2\2j\3\2\2\2\2l\3\2\2"+
-    "\2\2n\3\2\2\2\2p\3\2\2\2\2r\3\2\2\2\2t\3\2\2\2\2v\3\2\2\2\2x\3\2\2\2\2"+
-    "z\3\2\2\2\2|\3\2\2\2\2~\3\2\2\2\2\u0080\3\2\2\2\2\u0082\3\2\2\2\2\u0084"+
-    "\3\2\2\2\2\u0086\3\2\2\2\2\u0088\3\2\2\2\2\u008a\3\2\2\2\2\u008c\3\2\2"+
-    "\2\2\u008e\3\2\2\2\2\u0090\3\2\2\2\2\u0092\3\2\2\2\2\u0094\3\2\2\2\2\u0096"+
-    "\3\2\2\2\2\u0098\3\2\2\2\2\u009a\3\2\2\2\3\u009c\3\2\2\2\3\u009e\3\2\2"+
-    "\2\4\u00a1\3\2\2\2\6\u00bc\3\2\2\2\b\u00c0\3\2\2\2\n\u00c2\3\2\2\2\f\u00c4"+
-    "\3\2\2\2\16\u00c6\3\2\2\2\20\u00c8\3\2\2\2\22\u00ca\3\2\2\2\24\u00cc\3"+
-    "\2\2\2\26\u00d0\3\2\2\2\30\u00d2\3\2\2\2\32\u00d4\3\2\2\2\34\u00d7\3\2"+
-    "\2\2\36\u00dc\3\2\2\2 \u00e2\3\2\2\2\"\u00e5\3\2\2\2$\u00e9\3\2\2\2&\u00f2"+
-    "\3\2\2\2(\u00f8\3\2\2\2*\u00ff\3\2\2\2,\u0103\3\2\2\2.\u0107\3\2\2\2\60"+
-    "\u010d\3\2\2\2\62\u0113\3\2\2\2\64\u0118\3\2\2\2\66\u011a\3\2\2\28\u011c"+
-    "\3\2\2\2:\u011e\3\2\2\2<\u0121\3\2\2\2>\u0123\3\2\2\2@\u0125\3\2\2\2B"+
-    "\u0127\3\2\2\2D\u012a\3\2\2\2F\u012d\3\2\2\2H\u0131\3\2\2\2J\u0133\3\2"+
-    "\2\2L\u0136\3\2\2\2N\u0138\3\2\2\2P\u013b\3\2\2\2R\u013e\3\2\2\2T\u0142"+
-    "\3\2\2\2V\u0145\3\2\2\2X\u0149\3\2\2\2Z\u014b\3\2\2\2\\\u014d\3\2\2\2"+
-    "^\u014f\3\2\2\2`\u0152\3\2\2\2b\u0155\3\2\2\2d\u0157\3\2\2\2f\u0159\3"+
-    "\2\2\2h\u015c\3\2\2\2j\u015f\3\2\2\2l\u0162\3\2\2\2n\u0165\3\2\2\2p\u0167"+
-    "\3\2\2\2r\u016a\3\2\2\2t\u016d\3\2\2\2v\u0170\3\2\2\2x\u0173\3\2\2\2z"+
-    "\u0176\3\2\2\2|\u0179\3\2\2\2~\u017c\3\2\2\2\u0080\u017f\3\2\2\2\u0082"+
-    "\u0183\3\2\2\2\u0084\u0187\3\2\2\2\u0086\u018c\3\2\2\2\u0088\u0195\3\2"+
-    "\2\2\u008a\u01a7\3\2\2\2\u008c\u01b4\3\2\2\2\u008e\u01e4\3\2\2\2\u0090"+
-    "\u01e6\3\2\2\2\u0092\u01f1\3\2\2\2\u0094\u01f6\3\2\2\2\u0096\u01fc\3\2"+
-    "\2\2\u0098\u0201\3\2\2\2\u009a\u020c\3\2\2\2\u009c\u021b\3\2\2\2\u009e"+
-    "\u021f\3\2\2\2\u00a0\u00a2\t\2\2\2\u00a1\u00a0\3\2\2\2\u00a2\u00a3\3\2"+
-    "\2\2\u00a3\u00a1\3\2\2\2\u00a3\u00a4\3\2\2\2\u00a4\u00a5\3\2\2\2\u00a5"+
-    "\u00a6\b\2\2\2\u00a6\5\3\2\2\2\u00a7\u00a8\7\61\2\2\u00a8\u00a9\7\61\2"+
-    "\2\u00a9\u00ad\3\2\2\2\u00aa\u00ac\13\2\2\2\u00ab\u00aa\3\2\2\2\u00ac"+
-    "\u00af\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ad\u00ab\3\2\2\2\u00ae\u00b0\3\2"+
-    "\2\2\u00af\u00ad\3\2\2\2\u00b0\u00bd\t\3\2\2\u00b1\u00b2\7\61\2\2\u00b2"+
-    "\u00b3\7,\2\2\u00b3\u00b7\3\2\2\2\u00b4\u00b6\13\2\2\2\u00b5\u00b4\3\2"+
-    "\2\2\u00b6\u00b9\3\2\2\2\u00b7\u00b8\3\2\2\2\u00b7\u00b5\3\2\2\2\u00b8"+
-    "\u00ba\3\2\2\2\u00b9\u00b7\3\2\2\2\u00ba\u00bb\7,\2\2\u00bb\u00bd\7\61"+
-    "\2\2\u00bc\u00a7\3\2\2\2\u00bc\u00b1\3\2\2\2\u00bd\u00be\3\2\2\2\u00be"+
-    "\u00bf\b\3\2\2\u00bf\7\3\2\2\2\u00c0\u00c1\7}\2\2\u00c1\t\3\2\2\2\u00c2"+
-    "\u00c3\7\177\2\2\u00c3\13\3\2\2\2\u00c4\u00c5\7]\2\2\u00c5\r\3\2\2\2\u00c6"+
-    "\u00c7\7_\2\2\u00c7\17\3\2\2\2\u00c8\u00c9\7*\2\2\u00c9\21\3\2\2\2\u00ca"+
-    "\u00cb\7+\2\2\u00cb\23\3\2\2\2\u00cc\u00cd\7\60\2\2\u00cd\u00ce\3\2\2"+
-    "\2\u00ce\u00cf\b\n\3\2\u00cf\25\3\2\2\2\u00d0\u00d1\7.\2\2\u00d1\27\3"+
-    "\2\2\2\u00d2\u00d3\7=\2\2\u00d3\31\3\2\2\2\u00d4\u00d5\7k\2\2\u00d5\u00d6"+
-    "\7h\2\2\u00d6\33\3\2\2\2\u00d7\u00d8\7g\2\2\u00d8\u00d9\7n\2\2\u00d9\u00da"+
-    "\7u\2\2\u00da\u00db\7g\2\2\u00db\35\3\2\2\2\u00dc\u00dd\7y\2\2\u00dd\u00de"+
-    "\7j\2\2\u00de\u00df\7k\2\2\u00df\u00e0\7n\2\2\u00e0\u00e1\7g\2\2\u00e1"+
-    "\37\3\2\2\2\u00e2\u00e3\7f\2\2\u00e3\u00e4\7q\2\2\u00e4!\3\2\2\2\u00e5"+
-    "\u00e6\7h\2\2\u00e6\u00e7\7q\2\2\u00e7\u00e8\7t\2\2\u00e8#\3\2\2\2\u00e9"+
-    "\u00ea\7e\2\2\u00ea\u00eb\7q\2\2\u00eb\u00ec\7p\2\2\u00ec\u00ed\7v\2\2"+
-    "\u00ed\u00ee\7k\2\2\u00ee\u00ef\7p\2\2\u00ef\u00f0\7w\2\2\u00f0\u00f1"+
-    "\7g\2\2\u00f1%\3\2\2\2\u00f2\u00f3\7d\2\2\u00f3\u00f4\7t\2\2\u00f4\u00f5"+
-    "\7g\2\2\u00f5\u00f6\7c\2\2\u00f6\u00f7\7m\2\2\u00f7\'\3\2\2\2\u00f8\u00f9"+
-    "\7t\2\2\u00f9\u00fa\7g\2\2\u00fa\u00fb\7v\2\2\u00fb\u00fc\7w\2\2\u00fc"+
-    "\u00fd\7t\2\2\u00fd\u00fe\7p\2\2\u00fe)\3\2\2\2\u00ff\u0100\7p\2\2\u0100"+
-    "\u0101\7g\2\2\u0101\u0102\7y\2\2\u0102+\3\2\2\2\u0103\u0104\7v\2\2\u0104"+
-    "\u0105\7t\2\2\u0105\u0106\7{\2\2\u0106-\3\2\2\2\u0107\u0108\7e\2\2\u0108"+
-    "\u0109\7c\2\2\u0109\u010a\7v\2\2\u010a\u010b\7e\2\2\u010b\u010c\7j\2\2"+
-    "\u010c/\3\2\2\2\u010d\u010e\7v\2\2\u010e\u010f\7j\2\2\u010f\u0110\7t\2"+
-    "\2\u0110\u0111\7q\2\2\u0111\u0112\7y\2\2\u0112\61\3\2\2\2\u0113\u0114"+
-    "\7v\2\2\u0114\u0115\7j\2\2\u0115\u0116\7k\2\2\u0116\u0117\7u\2\2\u0117"+
-    "\63\3\2\2\2\u0118\u0119\7#\2\2\u0119\65\3\2\2\2\u011a\u011b\7\u0080\2"+
-    "\2\u011b\67\3\2\2\2\u011c\u011d\7,\2\2\u011d9\3\2\2\2\u011e\u011f\7\61"+
-    "\2\2\u011f\u0120\6\35\2\2\u0120;\3\2\2\2\u0121\u0122\7\'\2\2\u0122=\3"+
-    "\2\2\2\u0123\u0124\7-\2\2\u0124?\3\2\2\2\u0125\u0126\7/\2\2\u0126A\3\2"+
-    "\2\2\u0127\u0128\7>\2\2\u0128\u0129\7>\2\2\u0129C\3\2\2\2\u012a\u012b"+
-    "\7@\2\2\u012b\u012c\7@\2\2\u012cE\3\2\2\2\u012d\u012e\7@\2\2\u012e\u012f"+
-    "\7@\2\2\u012f\u0130\7@\2\2\u0130G\3\2\2\2\u0131\u0132\7>\2\2\u0132I\3"+
-    "\2\2\2\u0133\u0134\7>\2\2\u0134\u0135\7?\2\2\u0135K\3\2\2\2\u0136\u0137"+
-    "\7@\2\2\u0137M\3\2\2\2\u0138\u0139\7@\2\2\u0139\u013a\7?\2\2\u013aO\3"+
-    "\2\2\2\u013b\u013c\7?\2\2\u013c\u013d\7?\2\2\u013dQ\3\2\2\2\u013e\u013f"+
-    "\7?\2\2\u013f\u0140\7?\2\2\u0140\u0141\7?\2\2\u0141S\3\2\2\2\u0142\u0143"+
-    "\7#\2\2\u0143\u0144\7?\2\2\u0144U\3\2\2\2\u0145\u0146\7#\2\2\u0146\u0147"+
-    "\7?\2\2\u0147\u0148\7?\2\2\u0148W\3\2\2\2\u0149\u014a\7(\2\2\u014aY\3"+
-    "\2\2\2\u014b\u014c\7`\2\2\u014c[\3\2\2\2\u014d\u014e\7~\2\2\u014e]\3\2"+
-    "\2\2\u014f\u0150\7(\2\2\u0150\u0151\7(\2\2\u0151_\3\2\2\2\u0152\u0153"+
-    "\7~\2\2\u0153\u0154\7~\2\2\u0154a\3\2\2\2\u0155\u0156\7A\2\2\u0156c\3"+
-    "\2\2\2\u0157\u0158\7<\2\2\u0158e\3\2\2\2\u0159\u015a\7<\2\2\u015a\u015b"+
-    "\7<\2\2\u015bg\3\2\2\2\u015c\u015d\7/\2\2\u015d\u015e\7@\2\2\u015ei\3"+
-    "\2\2\2\u015f\u0160\7-\2\2\u0160\u0161\7-\2\2\u0161k\3\2\2\2\u0162\u0163"+
-    "\7/\2\2\u0163\u0164\7/\2\2\u0164m\3\2\2\2\u0165\u0166\7?\2\2\u0166o\3"+
-    "\2\2\2\u0167\u0168\7-\2\2\u0168\u0169\7?\2\2\u0169q\3\2\2\2\u016a\u016b"+
-    "\7/\2\2\u016b\u016c\7?\2\2\u016cs\3\2\2\2\u016d\u016e\7,\2\2\u016e\u016f"+
-    "\7?\2\2\u016fu\3\2\2\2\u0170\u0171\7\61\2\2\u0171\u0172\7?\2\2\u0172w"+
-    "\3\2\2\2\u0173\u0174\7\'\2\2\u0174\u0175\7?\2\2\u0175y\3\2\2\2\u0176\u0177"+
-    "\7(\2\2\u0177\u0178\7?\2\2\u0178{\3\2\2\2\u0179\u017a\7`\2\2\u017a\u017b"+
-    "\7?\2\2\u017b}\3\2\2\2\u017c\u017d\7~\2\2\u017d\u017e\7?\2\2\u017e\177"+
-    "\3\2\2\2\u017f\u0180\7>\2\2\u0180\u0181\7>\2\2\u0181\u0182\7?\2\2\u0182"+
-    "\u0081\3\2\2\2\u0183\u0184\7@\2\2\u0184\u0185\7@\2\2\u0185\u0186\7?\2"+
-    "\2\u0186\u0083\3\2\2\2\u0187\u0188\7@\2\2\u0188\u0189\7@\2\2\u0189\u018a"+
-    "\7@\2\2\u018a\u018b\7?\2\2\u018b\u0085\3\2\2\2\u018c\u018e\7\62\2\2\u018d"+
-    "\u018f\t\4\2\2\u018e\u018d\3\2\2\2\u018f\u0190\3\2\2\2\u0190\u018e\3\2"+
-    "\2\2\u0190\u0191\3\2\2\2\u0191\u0193\3\2\2\2\u0192\u0194\t\5\2\2\u0193"+
-    "\u0192\3\2\2\2\u0193\u0194\3\2\2\2\u0194\u0087\3\2\2\2\u0195\u0196\7\62"+
-    "\2\2\u0196\u0198\t\6\2\2\u0197\u0199\t\7\2\2\u0198\u0197\3\2\2\2\u0199"+
-    "\u019a\3\2\2\2\u019a\u0198\3\2\2\2\u019a\u019b\3\2\2\2\u019b\u019d\3\2"+
-    "\2\2\u019c\u019e\t\5\2\2\u019d\u019c\3\2\2\2\u019d\u019e\3\2\2\2\u019e"+
-    "\u0089\3\2\2\2\u019f\u01a8\7\62\2\2\u01a0\u01a4\t\b\2\2\u01a1\u01a3\t"+
-    "\t\2\2\u01a2\u01a1\3\2\2\2\u01a3\u01a6\3\2\2\2\u01a4\u01a2\3\2\2\2\u01a4"+
-    "\u01a5\3\2\2\2\u01a5\u01a8\3\2\2\2\u01a6\u01a4\3\2\2\2\u01a7\u019f\3\2"+
-    "\2\2\u01a7\u01a0\3\2\2\2\u01a8\u01aa\3\2\2\2\u01a9\u01ab\t\n\2\2\u01aa"+
-    "\u01a9\3\2\2\2\u01aa\u01ab\3\2\2\2\u01ab\u008b\3\2\2\2\u01ac\u01b5\7\62"+
-    "\2\2\u01ad\u01b1\t\b\2\2\u01ae\u01b0\t\t\2\2\u01af\u01ae\3\2\2\2\u01b0"+
-    "\u01b3\3\2\2\2\u01b1\u01af\3\2\2\2\u01b1\u01b2\3\2\2\2\u01b2\u01b5\3\2"+
-    "\2\2\u01b3\u01b1\3\2\2\2\u01b4\u01ac\3\2\2\2\u01b4\u01ad\3\2\2\2\u01b5"+
-    "\u01bc\3\2\2\2\u01b6\u01b8\5\24\n\2\u01b7\u01b9\t\t\2\2\u01b8\u01b7\3"+
-    "\2\2\2\u01b9\u01ba\3\2\2\2\u01ba\u01b8\3\2\2\2\u01ba\u01bb\3\2\2\2\u01bb"+
-    "\u01bd\3\2\2\2\u01bc\u01b6\3\2\2\2\u01bc\u01bd\3\2\2\2\u01bd\u01c7\3\2"+
-    "\2\2\u01be\u01c0\t\13\2\2\u01bf\u01c1\t\f\2\2\u01c0\u01bf\3\2\2\2\u01c0"+
-    "\u01c1\3\2\2\2\u01c1\u01c3\3\2\2\2\u01c2\u01c4\t\t\2\2\u01c3\u01c2\3\2"+
-    "\2\2\u01c4\u01c5\3\2\2\2\u01c5\u01c3\3\2\2\2\u01c5\u01c6\3\2\2\2\u01c6"+
-    "\u01c8\3\2\2\2\u01c7\u01be\3\2\2\2\u01c7\u01c8\3\2\2\2\u01c8\u01ca\3\2"+
-    "\2\2\u01c9\u01cb\t\r\2\2\u01ca\u01c9\3\2\2\2\u01ca\u01cb\3\2\2\2\u01cb"+
-    "\u008d\3\2\2\2\u01cc\u01d4\7$\2\2\u01cd\u01ce\7^\2\2\u01ce\u01d3\7$\2"+
-    "\2\u01cf\u01d0\7^\2\2\u01d0\u01d3\7^\2\2\u01d1\u01d3\n\16\2\2\u01d2\u01cd"+
-    "\3\2\2\2\u01d2\u01cf\3\2\2\2\u01d2\u01d1\3\2\2\2\u01d3\u01d6\3\2\2\2\u01d4"+
-    "\u01d5\3\2\2\2\u01d4\u01d2\3\2\2\2\u01d5\u01d7\3\2\2\2\u01d6\u01d4\3\2"+
-    "\2\2\u01d7\u01e5\7$\2\2\u01d8\u01e0\7)\2\2\u01d9\u01da\7^\2\2\u01da\u01df"+
-    "\7)\2\2\u01db\u01dc\7^\2\2\u01dc\u01df\7^\2\2\u01dd\u01df\n\16\2\2\u01de"+
-    "\u01d9\3\2\2\2\u01de\u01db\3\2\2\2\u01de\u01dd\3\2\2\2\u01df\u01e2\3\2"+
-    "\2\2\u01e0\u01e1\3\2\2\2\u01e0\u01de\3\2\2\2\u01e1\u01e3\3\2\2\2\u01e2"+
-    "\u01e0\3\2\2\2\u01e3\u01e5\7)\2\2\u01e4\u01cc\3\2\2\2\u01e4\u01d8\3\2"+
-    "\2\2\u01e5\u008f\3\2\2\2\u01e6\u01ea\7\61\2\2\u01e7\u01eb\n\17\2\2\u01e8"+
-    "\u01e9\7^\2\2\u01e9\u01eb\n\20\2\2\u01ea\u01e7\3\2\2\2\u01ea\u01e8\3\2"+
-    "\2\2\u01eb\u01ec\3\2\2\2\u01ec\u01ea\3\2\2\2\u01ec\u01ed\3\2\2\2\u01ed"+
-    "\u01ee\3\2\2\2\u01ee\u01ef\7\61\2\2\u01ef\u01f0\6H\3\2\u01f0\u0091\3\2"+
-    "\2\2\u01f1\u01f2\7v\2\2\u01f2\u01f3\7t\2\2\u01f3\u01f4\7w\2\2\u01f4\u01f5"+
-    "\7g\2\2\u01f5\u0093\3\2\2\2\u01f6\u01f7\7h\2\2\u01f7\u01f8\7c\2\2\u01f8"+
-    "\u01f9\7n\2\2\u01f9\u01fa\7u\2\2\u01fa\u01fb\7g\2\2\u01fb\u0095\3\2\2"+
-    "\2\u01fc\u01fd\7p\2\2\u01fd\u01fe\7w\2\2\u01fe\u01ff\7n\2\2\u01ff\u0200"+
-    "\7n\2\2\u0200\u0097\3\2\2\2\u0201\u0207\5\u009aM\2\u0202\u0203\5\24\n"+
-    "\2\u0203\u0204\5\u009aM\2\u0204\u0206\3\2\2\2\u0205\u0202\3\2\2\2\u0206"+
-    "\u0209\3\2\2\2\u0207\u0205\3\2\2\2\u0207\u0208\3\2\2\2\u0208\u020a\3\2"+
-    "\2\2\u0209\u0207\3\2\2\2\u020a\u020b\6L\4\2\u020b\u0099\3\2\2\2\u020c"+
-    "\u0210\t\21\2\2\u020d\u020f\t\22\2\2\u020e\u020d\3\2\2\2\u020f\u0212\3"+
-    "\2\2\2\u0210\u020e\3\2\2\2\u0210\u0211\3\2\2\2\u0211\u009b\3\2\2\2\u0212"+
-    "\u0210\3\2\2\2\u0213\u021c\7\62\2\2\u0214\u0218\t\b\2\2\u0215\u0217\t"+
-    "\t\2\2\u0216\u0215\3\2\2\2\u0217\u021a\3\2\2\2\u0218\u0216\3\2\2\2\u0218"+
-    "\u0219\3\2\2\2\u0219\u021c\3\2\2\2\u021a\u0218\3\2\2\2\u021b\u0213\3\2"+
-    "\2\2\u021b\u0214\3\2\2\2\u021c\u021d\3\2\2\2\u021d\u021e\bN\4\2\u021e"+
-    "\u009d\3\2\2\2\u021f\u0223\t\21\2\2\u0220\u0222\t\22\2\2\u0221\u0220\3"+
-    "\2\2\2\u0222\u0225\3\2\2\2\u0223\u0221\3\2\2\2\u0223\u0224\3\2\2\2\u0224"+
-    "\u0226\3\2\2\2\u0225\u0223\3\2\2\2\u0226\u0227\bO\4\2\u0227\u009f\3\2"+
-    "\2\2#\2\3\u00a3\u00ad\u00b7\u00bc\u0190\u0193\u019a\u019d\u01a4\u01a7"+
-    "\u01aa\u01b1\u01b4\u01ba\u01bc\u01c0\u01c5\u01c7\u01ca\u01d2\u01d4\u01de"+
-    "\u01e0\u01e4\u01ea\u01ec\u0207\u0210\u0218\u021b\u0223\5\b\2\2\4\3\2\4"+
-    "\2\2";
+    "I\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\3\2\6\2\u00a6\n\2"+
+    "\r\2\16\2\u00a7\3\2\3\2\3\3\3\3\3\3\3\3\7\3\u00b0\n\3\f\3\16\3\u00b3\13"+
+    "\3\3\3\3\3\3\3\3\3\3\3\7\3\u00ba\n\3\f\3\16\3\u00bd\13\3\3\3\3\3\5\3\u00c1"+
+    "\n\3\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3"+
+    "\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3\17\3\17"+
+    "\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\22\3\22\3\22"+
+    "\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24"+
+    "\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\27"+
+    "\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\31"+
+    "\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\35\3\35\3\35\3\36\3\36\3\37"+
+    "\3\37\3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3#\3$\3$\3%\3%\3%\3&\3&\3\'"+
+    "\3\'\3\'\3(\3(\3(\3)\3)\3)\3)\3*\3*\3*\3+\3+\3+\3+\3,\3,\3-\3-\3.\3.\3"+
+    "/\3/\3/\3\60\3\60\3\60\3\61\3\61\3\62\3\62\3\63\3\63\3\63\3\64\3\64\3"+
+    "\64\3\65\3\65\3\65\3\66\3\66\3\66\3\66\3\67\3\67\3\67\38\38\38\39\39\3"+
+    ":\3:\3:\3;\3;\3;\3<\3<\3<\3=\3=\3=\3>\3>\3>\3?\3?\3?\3@\3@\3@\3A\3A\3"+
+    "A\3B\3B\3B\3B\3C\3C\3C\3C\3D\3D\3D\3D\3D\3E\3E\6E\u019a\nE\rE\16E\u019b"+
+    "\3E\5E\u019f\nE\3F\3F\3F\6F\u01a4\nF\rF\16F\u01a5\3F\5F\u01a9\nF\3G\3"+
+    "G\3G\7G\u01ae\nG\fG\16G\u01b1\13G\5G\u01b3\nG\3G\5G\u01b6\nG\3H\3H\3H"+
+    "\7H\u01bb\nH\fH\16H\u01be\13H\5H\u01c0\nH\3H\3H\6H\u01c4\nH\rH\16H\u01c5"+
+    "\5H\u01c8\nH\3H\3H\5H\u01cc\nH\3H\6H\u01cf\nH\rH\16H\u01d0\5H\u01d3\n"+
+    "H\3H\5H\u01d6\nH\3I\3I\3I\3I\3I\3I\7I\u01de\nI\fI\16I\u01e1\13I\3I\3I"+
+    "\3I\3I\3I\3I\3I\7I\u01ea\nI\fI\16I\u01ed\13I\3I\5I\u01f0\nI\3J\3J\3J\3"+
+    "J\6J\u01f6\nJ\rJ\16J\u01f7\3J\3J\3J\3K\3K\3K\3K\3K\3L\3L\3L\3L\3L\3L\3"+
+    "M\3M\3M\3M\3M\3N\3N\3N\3N\7N\u0211\nN\fN\16N\u0214\13N\3N\3N\3O\3O\7O"+
+    "\u021a\nO\fO\16O\u021d\13O\3P\3P\3P\7P\u0222\nP\fP\16P\u0225\13P\5P\u0227"+
+    "\nP\3P\3P\3Q\3Q\7Q\u022d\nQ\fQ\16Q\u0230\13Q\3Q\3Q\6\u00b1\u00bb\u01df"+
+    "\u01eb\2R\4\3\6\4\b\5\n\6\f\7\16\b\20\t\22\n\24\13\26\f\30\r\32\16\34"+
+    "\17\36\20 \21\"\22$\23&\24(\25*\26,\27.\30\60\31\62\32\64\33\66\348\35"+
+    ":\36<\37> @!B\"D#F$H%J&L\'N(P)R*T+V,X-Z.\\/^\60`\61b\62d\63f\64h\65j\66"+
+    "l\67n8p9r:t;v<x=z>|?~@\u0080A\u0082B\u0084C\u0086D\u0088E\u008aF\u008c"+
+    "G\u008eH\u0090I\u0092J\u0094K\u0096L\u0098M\u009aN\u009cO\u009eP\u00a0"+
+    "Q\u00a2R\4\2\3\23\5\2\13\f\17\17\"\"\4\2\f\f\17\17\3\2\629\4\2NNnn\4\2"+
+    "ZZzz\5\2\62;CHch\3\2\63;\3\2\62;\b\2FFHHNNffhhnn\4\2GGgg\4\2--//\4\2H"+
+    "Hhh\4\2$$^^\4\2\f\f\61\61\3\2\f\f\5\2C\\aac|\6\2\62;C\\aac|\u0252\2\4"+
+    "\3\2\2\2\2\6\3\2\2\2\2\b\3\2\2\2\2\n\3\2\2\2\2\f\3\2\2\2\2\16\3\2\2\2"+
+    "\2\20\3\2\2\2\2\22\3\2\2\2\2\24\3\2\2\2\2\26\3\2\2\2\2\30\3\2\2\2\2\32"+
+    "\3\2\2\2\2\34\3\2\2\2\2\36\3\2\2\2\2 \3\2\2\2\2\"\3\2\2\2\2$\3\2\2\2\2"+
+    "&\3\2\2\2\2(\3\2\2\2\2*\3\2\2\2\2,\3\2\2\2\2.\3\2\2\2\2\60\3\2\2\2\2\62"+
+    "\3\2\2\2\2\64\3\2\2\2\2\66\3\2\2\2\28\3\2\2\2\2:\3\2\2\2\2<\3\2\2\2\2"+
+    ">\3\2\2\2\2@\3\2\2\2\2B\3\2\2\2\2D\3\2\2\2\2F\3\2\2\2\2H\3\2\2\2\2J\3"+
+    "\2\2\2\2L\3\2\2\2\2N\3\2\2\2\2P\3\2\2\2\2R\3\2\2\2\2T\3\2\2\2\2V\3\2\2"+
+    "\2\2X\3\2\2\2\2Z\3\2\2\2\2\\\3\2\2\2\2^\3\2\2\2\2`\3\2\2\2\2b\3\2\2\2"+
+    "\2d\3\2\2\2\2f\3\2\2\2\2h\3\2\2\2\2j\3\2\2\2\2l\3\2\2\2\2n\3\2\2\2\2p"+
+    "\3\2\2\2\2r\3\2\2\2\2t\3\2\2\2\2v\3\2\2\2\2x\3\2\2\2\2z\3\2\2\2\2|\3\2"+
+    "\2\2\2~\3\2\2\2\2\u0080\3\2\2\2\2\u0082\3\2\2\2\2\u0084\3\2\2\2\2\u0086"+
+    "\3\2\2\2\2\u0088\3\2\2\2\2\u008a\3\2\2\2\2\u008c\3\2\2\2\2\u008e\3\2\2"+
+    "\2\2\u0090\3\2\2\2\2\u0092\3\2\2\2\2\u0094\3\2\2\2\2\u0096\3\2\2\2\2\u0098"+
+    "\3\2\2\2\2\u009a\3\2\2\2\2\u009c\3\2\2\2\2\u009e\3\2\2\2\3\u00a0\3\2\2"+
+    "\2\3\u00a2\3\2\2\2\4\u00a5\3\2\2\2\6\u00c0\3\2\2\2\b\u00c4\3\2\2\2\n\u00c6"+
+    "\3\2\2\2\f\u00c8\3\2\2\2\16\u00ca\3\2\2\2\20\u00cc\3\2\2\2\22\u00ce\3"+
+    "\2\2\2\24\u00d0\3\2\2\2\26\u00d4\3\2\2\2\30\u00d6\3\2\2\2\32\u00d8\3\2"+
+    "\2\2\34\u00db\3\2\2\2\36\u00e0\3\2\2\2 \u00e6\3\2\2\2\"\u00e9\3\2\2\2"+
+    "$\u00ed\3\2\2\2&\u00f6\3\2\2\2(\u00fc\3\2\2\2*\u0103\3\2\2\2,\u0107\3"+
+    "\2\2\2.\u010b\3\2\2\2\60\u0111\3\2\2\2\62\u0117\3\2\2\2\64\u011c\3\2\2"+
+    "\2\66\u011e\3\2\2\28\u0120\3\2\2\2:\u0122\3\2\2\2<\u0125\3\2\2\2>\u0127"+
+    "\3\2\2\2@\u0129\3\2\2\2B\u012b\3\2\2\2D\u012e\3\2\2\2F\u0131\3\2\2\2H"+
+    "\u0135\3\2\2\2J\u0137\3\2\2\2L\u013a\3\2\2\2N\u013c\3\2\2\2P\u013f\3\2"+
+    "\2\2R\u0142\3\2\2\2T\u0146\3\2\2\2V\u0149\3\2\2\2X\u014d\3\2\2\2Z\u014f"+
+    "\3\2\2\2\\\u0151\3\2\2\2^\u0153\3\2\2\2`\u0156\3\2\2\2b\u0159\3\2\2\2"+
+    "d\u015b\3\2\2\2f\u015d\3\2\2\2h\u0160\3\2\2\2j\u0163\3\2\2\2l\u0166\3"+
+    "\2\2\2n\u016a\3\2\2\2p\u016d\3\2\2\2r\u0170\3\2\2\2t\u0172\3\2\2\2v\u0175"+
+    "\3\2\2\2x\u0178\3\2\2\2z\u017b\3\2\2\2|\u017e\3\2\2\2~\u0181\3\2\2\2\u0080"+
+    "\u0184\3\2\2\2\u0082\u0187\3\2\2\2\u0084\u018a\3\2\2\2\u0086\u018e\3\2"+
+    "\2\2\u0088\u0192\3\2\2\2\u008a\u0197\3\2\2\2\u008c\u01a0\3\2\2\2\u008e"+
+    "\u01b2\3\2\2\2\u0090\u01bf\3\2\2\2\u0092\u01ef\3\2\2\2\u0094\u01f1\3\2"+
+    "\2\2\u0096\u01fc\3\2\2\2\u0098\u0201\3\2\2\2\u009a\u0207\3\2\2\2\u009c"+
+    "\u020c\3\2\2\2\u009e\u0217\3\2\2\2\u00a0\u0226\3\2\2\2\u00a2\u022a\3\2"+
+    "\2\2\u00a4\u00a6\t\2\2\2\u00a5\u00a4\3\2\2\2\u00a6\u00a7\3\2\2\2\u00a7"+
+    "\u00a5\3\2\2\2\u00a7\u00a8\3\2\2\2\u00a8\u00a9\3\2\2\2\u00a9\u00aa\b\2"+
+    "\2\2\u00aa\5\3\2\2\2\u00ab\u00ac\7\61\2\2\u00ac\u00ad\7\61\2\2\u00ad\u00b1"+
+    "\3\2\2\2\u00ae\u00b0\13\2\2\2\u00af\u00ae\3\2\2\2\u00b0\u00b3\3\2\2\2"+
+    "\u00b1\u00b2\3\2\2\2\u00b1\u00af\3\2\2\2\u00b2\u00b4\3\2\2\2\u00b3\u00b1"+
+    "\3\2\2\2\u00b4\u00c1\t\3\2\2\u00b5\u00b6\7\61\2\2\u00b6\u00b7\7,\2\2\u00b7"+
+    "\u00bb\3\2\2\2\u00b8\u00ba\13\2\2\2\u00b9\u00b8\3\2\2\2\u00ba\u00bd\3"+
+    "\2\2\2\u00bb\u00bc\3\2\2\2\u00bb\u00b9\3\2\2\2\u00bc\u00be\3\2\2\2\u00bd"+
+    "\u00bb\3\2\2\2\u00be\u00bf\7,\2\2\u00bf\u00c1\7\61\2\2\u00c0\u00ab\3\2"+
+    "\2\2\u00c0\u00b5\3\2\2\2\u00c1\u00c2\3\2\2\2\u00c2\u00c3\b\3\2\2\u00c3"+
+    "\7\3\2\2\2\u00c4\u00c5\7}\2\2\u00c5\t\3\2\2\2\u00c6\u00c7\7\177\2\2\u00c7"+
+    "\13\3\2\2\2\u00c8\u00c9\7]\2\2\u00c9\r\3\2\2\2\u00ca\u00cb\7_\2\2\u00cb"+
+    "\17\3\2\2\2\u00cc\u00cd\7*\2\2\u00cd\21\3\2\2\2\u00ce\u00cf\7+\2\2\u00cf"+
+    "\23\3\2\2\2\u00d0\u00d1\7\60\2\2\u00d1\u00d2\3\2\2\2\u00d2\u00d3\b\n\3"+
+    "\2\u00d3\25\3\2\2\2\u00d4\u00d5\7.\2\2\u00d5\27\3\2\2\2\u00d6\u00d7\7"+
+    "=\2\2\u00d7\31\3\2\2\2\u00d8\u00d9\7k\2\2\u00d9\u00da\7h\2\2\u00da\33"+
+    "\3\2\2\2\u00db\u00dc\7g\2\2\u00dc\u00dd\7n\2\2\u00dd\u00de\7u\2\2\u00de"+
+    "\u00df\7g\2\2\u00df\35\3\2\2\2\u00e0\u00e1\7y\2\2\u00e1\u00e2\7j\2\2\u00e2"+
+    "\u00e3\7k\2\2\u00e3\u00e4\7n\2\2\u00e4\u00e5\7g\2\2\u00e5\37\3\2\2\2\u00e6"+
+    "\u00e7\7f\2\2\u00e7\u00e8\7q\2\2\u00e8!\3\2\2\2\u00e9\u00ea\7h\2\2\u00ea"+
+    "\u00eb\7q\2\2\u00eb\u00ec\7t\2\2\u00ec#\3\2\2\2\u00ed\u00ee\7e\2\2\u00ee"+
+    "\u00ef\7q\2\2\u00ef\u00f0\7p\2\2\u00f0\u00f1\7v\2\2\u00f1\u00f2\7k\2\2"+
+    "\u00f2\u00f3\7p\2\2\u00f3\u00f4\7w\2\2\u00f4\u00f5\7g\2\2\u00f5%\3\2\2"+
+    "\2\u00f6\u00f7\7d\2\2\u00f7\u00f8\7t\2\2\u00f8\u00f9\7g\2\2\u00f9\u00fa"+
+    "\7c\2\2\u00fa\u00fb\7m\2\2\u00fb\'\3\2\2\2\u00fc\u00fd\7t\2\2\u00fd\u00fe"+
+    "\7g\2\2\u00fe\u00ff\7v\2\2\u00ff\u0100\7w\2\2\u0100\u0101\7t\2\2\u0101"+
+    "\u0102\7p\2\2\u0102)\3\2\2\2\u0103\u0104\7p\2\2\u0104\u0105\7g\2\2\u0105"+
+    "\u0106\7y\2\2\u0106+\3\2\2\2\u0107\u0108\7v\2\2\u0108\u0109\7t\2\2\u0109"+
+    "\u010a\7{\2\2\u010a-\3\2\2\2\u010b\u010c\7e\2\2\u010c\u010d\7c\2\2\u010d"+
+    "\u010e\7v\2\2\u010e\u010f\7e\2\2\u010f\u0110\7j\2\2\u0110/\3\2\2\2\u0111"+
+    "\u0112\7v\2\2\u0112\u0113\7j\2\2\u0113\u0114\7t\2\2\u0114\u0115\7q\2\2"+
+    "\u0115\u0116\7y\2\2\u0116\61\3\2\2\2\u0117\u0118\7v\2\2\u0118\u0119\7"+
+    "j\2\2\u0119\u011a\7k\2\2\u011a\u011b\7u\2\2\u011b\63\3\2\2\2\u011c\u011d"+
+    "\7#\2\2\u011d\65\3\2\2\2\u011e\u011f\7\u0080\2\2\u011f\67\3\2\2\2\u0120"+
+    "\u0121\7,\2\2\u01219\3\2\2\2\u0122\u0123\7\61\2\2\u0123\u0124\6\35\2\2"+
+    "\u0124;\3\2\2\2\u0125\u0126\7\'\2\2\u0126=\3\2\2\2\u0127\u0128\7-\2\2"+
+    "\u0128?\3\2\2\2\u0129\u012a\7/\2\2\u012aA\3\2\2\2\u012b\u012c\7>\2\2\u012c"+
+    "\u012d\7>\2\2\u012dC\3\2\2\2\u012e\u012f\7@\2\2\u012f\u0130\7@\2\2\u0130"+
+    "E\3\2\2\2\u0131\u0132\7@\2\2\u0132\u0133\7@\2\2\u0133\u0134\7@\2\2\u0134"+
+    "G\3\2\2\2\u0135\u0136\7>\2\2\u0136I\3\2\2\2\u0137\u0138\7>\2\2\u0138\u0139"+
+    "\7?\2\2\u0139K\3\2\2\2\u013a\u013b\7@\2\2\u013bM\3\2\2\2\u013c\u013d\7"+
+    "@\2\2\u013d\u013e\7?\2\2\u013eO\3\2\2\2\u013f\u0140\7?\2\2\u0140\u0141"+
+    "\7?\2\2\u0141Q\3\2\2\2\u0142\u0143\7?\2\2\u0143\u0144\7?\2\2\u0144\u0145"+
+    "\7?\2\2\u0145S\3\2\2\2\u0146\u0147\7#\2\2\u0147\u0148\7?\2\2\u0148U\3"+
+    "\2\2\2\u0149\u014a\7#\2\2\u014a\u014b\7?\2\2\u014b\u014c\7?\2\2\u014c"+
+    "W\3\2\2\2\u014d\u014e\7(\2\2\u014eY\3\2\2\2\u014f\u0150\7`\2\2\u0150["+
+    "\3\2\2\2\u0151\u0152\7~\2\2\u0152]\3\2\2\2\u0153\u0154\7(\2\2\u0154\u0155"+
+    "\7(\2\2\u0155_\3\2\2\2\u0156\u0157\7~\2\2\u0157\u0158\7~\2\2\u0158a\3"+
+    "\2\2\2\u0159\u015a\7A\2\2\u015ac\3\2\2\2\u015b\u015c\7<\2\2\u015ce\3\2"+
+    "\2\2\u015d\u015e\7<\2\2\u015e\u015f\7<\2\2\u015fg\3\2\2\2\u0160\u0161"+
+    "\7/\2\2\u0161\u0162\7@\2\2\u0162i\3\2\2\2\u0163\u0164\7?\2\2\u0164\u0165"+
+    "\7\u0080\2\2\u0165k\3\2\2\2\u0166\u0167\7?\2\2\u0167\u0168\7?\2\2\u0168"+
+    "\u0169\7\u0080\2\2\u0169m\3\2\2\2\u016a\u016b\7-\2\2\u016b\u016c\7-\2"+
+    "\2\u016co\3\2\2\2\u016d\u016e\7/\2\2\u016e\u016f\7/\2\2\u016fq\3\2\2\2"+
+    "\u0170\u0171\7?\2\2\u0171s\3\2\2\2\u0172\u0173\7-\2\2\u0173\u0174\7?\2"+
+    "\2\u0174u\3\2\2\2\u0175\u0176\7/\2\2\u0176\u0177\7?\2\2\u0177w\3\2\2\2"+
+    "\u0178\u0179\7,\2\2\u0179\u017a\7?\2\2\u017ay\3\2\2\2\u017b\u017c\7\61"+
+    "\2\2\u017c\u017d\7?\2\2\u017d{\3\2\2\2\u017e\u017f\7\'\2\2\u017f\u0180"+
+    "\7?\2\2\u0180}\3\2\2\2\u0181\u0182\7(\2\2\u0182\u0183\7?\2\2\u0183\177"+
+    "\3\2\2\2\u0184\u0185\7`\2\2\u0185\u0186\7?\2\2\u0186\u0081\3\2\2\2\u0187"+
+    "\u0188\7~\2\2\u0188\u0189\7?\2\2\u0189\u0083\3\2\2\2\u018a\u018b\7>\2"+
+    "\2\u018b\u018c\7>\2\2\u018c\u018d\7?\2\2\u018d\u0085\3\2\2\2\u018e\u018f"+
+    "\7@\2\2\u018f\u0190\7@\2\2\u0190\u0191\7?\2\2\u0191\u0087\3\2\2\2\u0192"+
+    "\u0193\7@\2\2\u0193\u0194\7@\2\2\u0194\u0195\7@\2\2\u0195\u0196\7?\2\2"+
+    "\u0196\u0089\3\2\2\2\u0197\u0199\7\62\2\2\u0198\u019a\t\4\2\2\u0199\u0198"+
+    "\3\2\2\2\u019a\u019b\3\2\2\2\u019b\u0199\3\2\2\2\u019b\u019c\3\2\2\2\u019c"+
+    "\u019e\3\2\2\2\u019d\u019f\t\5\2\2\u019e\u019d\3\2\2\2\u019e\u019f\3\2"+
+    "\2\2\u019f\u008b\3\2\2\2\u01a0\u01a1\7\62\2\2\u01a1\u01a3\t\6\2\2\u01a2"+
+    "\u01a4\t\7\2\2\u01a3\u01a2\3\2\2\2\u01a4\u01a5\3\2\2\2\u01a5\u01a3\3\2"+
+    "\2\2\u01a5\u01a6\3\2\2\2\u01a6\u01a8\3\2\2\2\u01a7\u01a9\t\5\2\2\u01a8"+
+    "\u01a7\3\2\2\2\u01a8\u01a9\3\2\2\2\u01a9\u008d\3\2\2\2\u01aa\u01b3\7\62"+
+    "\2\2\u01ab\u01af\t\b\2\2\u01ac\u01ae\t\t\2\2\u01ad\u01ac\3\2\2\2\u01ae"+
+    "\u01b1\3\2\2\2\u01af\u01ad\3\2\2\2\u01af\u01b0\3\2\2\2\u01b0\u01b3\3\2"+
+    "\2\2\u01b1\u01af\3\2\2\2\u01b2\u01aa\3\2\2\2\u01b2\u01ab\3\2\2\2\u01b3"+
+    "\u01b5\3\2\2\2\u01b4\u01b6\t\n\2\2\u01b5\u01b4\3\2\2\2\u01b5\u01b6\3\2"+
+    "\2\2\u01b6\u008f\3\2\2\2\u01b7\u01c0\7\62\2\2\u01b8\u01bc\t\b\2\2\u01b9"+
+    "\u01bb\t\t\2\2\u01ba\u01b9\3\2\2\2\u01bb\u01be\3\2\2\2\u01bc\u01ba\3\2"+
+    "\2\2\u01bc\u01bd\3\2\2\2\u01bd\u01c0\3\2\2\2\u01be\u01bc\3\2\2\2\u01bf"+
+    "\u01b7\3\2\2\2\u01bf\u01b8\3\2\2\2\u01c0\u01c7\3\2\2\2\u01c1\u01c3\5\24"+
+    "\n\2\u01c2\u01c4\t\t\2\2\u01c3\u01c2\3\2\2\2\u01c4\u01c5\3\2\2\2\u01c5"+
+    "\u01c3\3\2\2\2\u01c5\u01c6\3\2\2\2\u01c6\u01c8\3\2\2\2\u01c7\u01c1\3\2"+
+    "\2\2\u01c7\u01c8\3\2\2\2\u01c8\u01d2\3\2\2\2\u01c9\u01cb\t\13\2\2\u01ca"+
+    "\u01cc\t\f\2\2\u01cb\u01ca\3\2\2\2\u01cb\u01cc\3\2\2\2\u01cc\u01ce\3\2"+
+    "\2\2\u01cd\u01cf\t\t\2\2\u01ce\u01cd\3\2\2\2\u01cf\u01d0\3\2\2\2\u01d0"+
+    "\u01ce\3\2\2\2\u01d0\u01d1\3\2\2\2\u01d1\u01d3\3\2\2\2\u01d2\u01c9\3\2"+
+    "\2\2\u01d2\u01d3\3\2\2\2\u01d3\u01d5\3\2\2\2\u01d4\u01d6\t\r\2\2\u01d5"+
+    "\u01d4\3\2\2\2\u01d5\u01d6\3\2\2\2\u01d6\u0091\3\2\2\2\u01d7\u01df\7$"+
+    "\2\2\u01d8\u01d9\7^\2\2\u01d9\u01de\7$\2\2\u01da\u01db\7^\2\2\u01db\u01de"+
+    "\7^\2\2\u01dc\u01de\n\16\2\2\u01dd\u01d8\3\2\2\2\u01dd\u01da\3\2\2\2\u01dd"+
+    "\u01dc\3\2\2\2\u01de\u01e1\3\2\2\2\u01df\u01e0\3\2\2\2\u01df\u01dd\3\2"+
+    "\2\2\u01e0\u01e2\3\2\2\2\u01e1\u01df\3\2\2\2\u01e2\u01f0\7$\2\2\u01e3"+
+    "\u01eb\7)\2\2\u01e4\u01e5\7^\2\2\u01e5\u01ea\7)\2\2\u01e6\u01e7\7^\2\2"+
+    "\u01e7\u01ea\7^\2\2\u01e8\u01ea\n\16\2\2\u01e9\u01e4\3\2\2\2\u01e9\u01e6"+
+    "\3\2\2\2\u01e9\u01e8\3\2\2\2\u01ea\u01ed\3\2\2\2\u01eb\u01ec\3\2\2\2\u01eb"+
+    "\u01e9\3\2\2\2\u01ec\u01ee\3\2\2\2\u01ed\u01eb\3\2\2\2\u01ee\u01f0\7)"+
+    "\2\2\u01ef\u01d7\3\2\2\2\u01ef\u01e3\3\2\2\2\u01f0\u0093\3\2\2\2\u01f1"+
+    "\u01f5\7\61\2\2\u01f2\u01f6\n\17\2\2\u01f3\u01f4\7^\2\2\u01f4\u01f6\n"+
+    "\20\2\2\u01f5\u01f2\3\2\2\2\u01f5\u01f3\3\2\2\2\u01f6\u01f7\3\2\2\2\u01f7"+
+    "\u01f5\3\2\2\2\u01f7\u01f8\3\2\2\2\u01f8\u01f9\3\2\2\2\u01f9\u01fa\7\61"+
+    "\2\2\u01fa\u01fb\6J\3\2\u01fb\u0095\3\2\2\2\u01fc\u01fd\7v\2\2\u01fd\u01fe"+
+    "\7t\2\2\u01fe\u01ff\7w\2\2\u01ff\u0200\7g\2\2\u0200\u0097\3\2\2\2\u0201"+
+    "\u0202\7h\2\2\u0202\u0203\7c\2\2\u0203\u0204\7n\2\2\u0204\u0205\7u\2\2"+
+    "\u0205\u0206\7g\2\2\u0206\u0099\3\2\2\2\u0207\u0208\7p\2\2\u0208\u0209"+
+    "\7w\2\2\u0209\u020a\7n\2\2\u020a\u020b\7n\2\2\u020b\u009b\3\2\2\2\u020c"+
+    "\u0212\5\u009eO\2\u020d\u020e\5\24\n\2\u020e\u020f\5\u009eO\2\u020f\u0211"+
+    "\3\2\2\2\u0210\u020d\3\2\2\2\u0211\u0214\3\2\2\2\u0212\u0210\3\2\2\2\u0212"+
+    "\u0213\3\2\2\2\u0213\u0215\3\2\2\2\u0214\u0212\3\2\2\2\u0215\u0216\6N"+
+    "\4\2\u0216\u009d\3\2\2\2\u0217\u021b\t\21\2\2\u0218\u021a\t\22\2\2\u0219"+
+    "\u0218\3\2\2\2\u021a\u021d\3\2\2\2\u021b\u0219\3\2\2\2\u021b\u021c\3\2"+
+    "\2\2\u021c\u009f\3\2\2\2\u021d\u021b\3\2\2\2\u021e\u0227\7\62\2\2\u021f"+
+    "\u0223\t\b\2\2\u0220\u0222\t\t\2\2\u0221\u0220\3\2\2\2\u0222\u0225\3\2"+
+    "\2\2\u0223\u0221\3\2\2\2\u0223\u0224\3\2\2\2\u0224\u0227\3\2\2\2\u0225"+
+    "\u0223\3\2\2\2\u0226\u021e\3\2\2\2\u0226\u021f\3\2\2\2\u0227\u0228\3\2"+
+    "\2\2\u0228\u0229\bP\4\2\u0229\u00a1\3\2\2\2\u022a\u022e\t\21\2\2\u022b"+
+    "\u022d\t\22\2\2\u022c\u022b\3\2\2\2\u022d\u0230\3\2\2\2\u022e\u022c\3"+
+    "\2\2\2\u022e\u022f\3\2\2\2\u022f\u0231\3\2\2\2\u0230\u022e\3\2\2\2\u0231"+
+    "\u0232\bQ\4\2\u0232\u00a3\3\2\2\2#\2\3\u00a7\u00b1\u00bb\u00c0\u019b\u019e"+
+    "\u01a5\u01a8\u01af\u01b2\u01b5\u01bc\u01bf\u01c5\u01c7\u01cb\u01d0\u01d2"+
+    "\u01d5\u01dd\u01df\u01e9\u01eb\u01ef\u01f5\u01f7\u0212\u021b\u0223\u0226"+
+    "\u022e\5\b\2\2\4\3\2\4\2\2";
   public static final ATN _ATN =
     new ATNDeserializer().deserialize(_serializedATN.toCharArray());
   static {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 195 - 173
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java


+ 4 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java

@@ -512,6 +512,10 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
             operation = Operation.ADD;
         } else if (ctx.SUB() != null) {
             operation = Operation.SUB;
+        } else if (ctx.FIND() != null) {
+            operation = Operation.FIND;
+        } else if (ctx.MATCH() != null) {
+            operation = Operation.MATCH;
         } else if (ctx.LSH() != null) {
             operation = Operation.LSH;
         } else if (ctx.RSH() != null) {

+ 32 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java

@@ -26,6 +26,7 @@ import org.elasticsearch.painless.Definition.Type;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.Operation;
+import org.elasticsearch.painless.WriterConstants;
 import org.elasticsearch.painless.Locals;
 
 /**
@@ -61,6 +62,10 @@ public final class EBinary extends AExpression {
             analyzeAdd(locals);
         } else if (operation == Operation.SUB) {
             analyzeSub(locals);
+        } else if (operation == Operation.FIND) {
+            analyzeRegexOp(locals);
+        } else if (operation == Operation.MATCH) {
+            analyzeRegexOp(locals);
         } else if (operation == Operation.LSH) {
             analyzeLSH(locals);
         } else if (operation == Operation.RSH) {
@@ -320,6 +325,21 @@ public final class EBinary extends AExpression {
         }
     }
 
+    private void analyzeRegexOp(Locals variables) {
+        left.analyze(variables);
+        right.analyze(variables);
+
+        left.expected = Definition.STRING_TYPE;
+        right.expected = Definition.PATTERN_TYPE;
+
+        left = left.cast(variables);
+        right = right.cast(variables);
+
+        // It'd be nice to be able to do constant folding here but we can't because constants aren't flowing through EChain
+        promote = Definition.BOOLEAN_TYPE;
+        actual = Definition.BOOLEAN_TYPE;
+    }
+
     private void analyzeLSH(Locals variables) {
         left.analyze(variables);
         right.analyze(variables);
@@ -607,6 +627,12 @@ public final class EBinary extends AExpression {
             if (!cat) {
                 writer.writeToStrings();
             }
+        } else if (operation == Operation.FIND) {
+            writeBuildMatcher(writer);
+            writer.invokeVirtual(Definition.MATCHER_TYPE.type, WriterConstants.MATCHER_FIND);
+        } else if (operation == Operation.MATCH) {
+            writeBuildMatcher(writer);
+            writer.invokeVirtual(Definition.MATCHER_TYPE.type, WriterConstants.MATCHER_MATCHES);
         } else {
             left.write(writer);
             right.write(writer);
@@ -620,4 +646,10 @@ public final class EBinary extends AExpression {
 
         writer.writeBranch(tru, fals);
     }
+
+    private void writeBuildMatcher(MethodWriter writer) {
+        right.write(writer);
+        left.write(writer);
+        writer.invokeVirtual(Definition.PATTERN_TYPE.type, WriterConstants.PATTERN_MATCHER);
+    }
 }

+ 4 - 6
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LRegex.java

@@ -34,8 +34,6 @@ import org.elasticsearch.painless.WriterConstants;
  * Represents a regex constant. All regexes are constants.
  */
 public final class LRegex extends ALink {
-    private static final Definition.Type PATTERN_TYPE = Definition.getType("Pattern");
-
     private final String pattern;
     private Constant constant;
 
@@ -60,8 +58,8 @@ public final class LRegex extends ALink {
             throw createError(new IllegalArgumentException("Regex constant may only be read [" + pattern + "]."));
         }
 
-        constant = locals.addConstant(location, PATTERN_TYPE, "regexAt$" + location.getOffset(), this::initializeConstant);
-        after = PATTERN_TYPE;
+        constant = locals.addConstant(location, Definition.PATTERN_TYPE, "regexAt$" + location.getOffset(), this::initializeConstant);
+        after = Definition.PATTERN_TYPE;
 
         return this;
     }
@@ -74,7 +72,7 @@ public final class LRegex extends ALink {
     @Override
     void load(MethodWriter writer) {
         writer.writeDebugInfo(location);
-        writer.getStatic(WriterConstants.CLASS_TYPE, constant.name, PATTERN_TYPE.type);
+        writer.getStatic(WriterConstants.CLASS_TYPE, constant.name, Definition.PATTERN_TYPE.type);
     }
 
     @Override
@@ -84,6 +82,6 @@ public final class LRegex extends ALink {
 
     private void initializeConstant(MethodWriter writer) {
         writer.push(pattern);
-        writer.invokeStatic(PATTERN_TYPE.type, WriterConstants.PATTERN_COMPILE);
+        writer.invokeStatic(Definition.PATTERN_TYPE.type, WriterConstants.PATTERN_COMPILE);
     }
 }

+ 26 - 0
modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.util.regex.txt

@@ -29,5 +29,31 @@ class Pattern -> java.util.regex.Pattern extends Object {
 }
 
 class Matcher -> java.util.regex.Matcher extends Object {
+  int end()
+  int end(int)
+  boolean find()
+  boolean find(int)
+  String group()
+  String group(int)
+  String namedGroup/group(String)
+  int groupCount()
+  boolean hasAnchoringBounds()
+  boolean hasTransparentBounds()
+  boolean hitEnd()
+  boolean lookingAt()
   boolean matches()
+  Pattern pattern()
+  String quoteReplacement(String)
+  Matcher region(int,int)
+  int regionEnd()
+  int regionStart()
+  String replaceAll(String)
+  String replaceFirst(String)
+  boolean requireEnd()
+  Matcher reset()
+  int start()
+  int start(int)
+  Matcher useAnchoringBounds(boolean)
+  Matcher usePattern(Pattern)
+  Matcher useTransparentBounds(boolean)
 }

+ 93 - 15
modules/lang-painless/src/test/java/org/elasticsearch/painless/RegexTests.java

@@ -19,53 +19,131 @@
 
 package org.elasticsearch.painless;
 
+import java.util.regex.PatternSyntaxException;
+
+import static java.util.Collections.singletonMap;
+import static org.hamcrest.Matchers.containsString;
+
 public class RegexTests extends ScriptTestCase {
     public void testPatternAfterReturn() {
-        assertEquals(true, exec("return /foo/.matcher(\"foo\").matches()"));
-        assertEquals(false, exec("return /foo/.matcher(\"bar\").matches()"));
+        assertEquals(true, exec("return 'foo' ==~ /foo/"));
+        assertEquals(false, exec("return 'bar' ==~ /foo/"));
     }
 
     public void testSlashesEscapePattern() {
-        assertEquals(true, exec("return /\\/\\//.matcher(\"//\").matches()"));
+        assertEquals(true, exec("return '//' ==~ /\\/\\//"));
     }
 
     public void testPatternAfterAssignment() {
-        assertEquals(true, exec("def a = /foo/; return a.matcher(\"foo\").matches()"));
+        assertEquals(true, exec("def a = /foo/; return 'foo' ==~ a"));
     }
 
     public void testPatternInIfStement() {
-        assertEquals(true, exec("if (/foo/.matcher(\"foo\").matches()) { return true } else { return false }"));
+        assertEquals(true, exec("if (/foo/.matcher('foo').matches()) { return true } else { return false }"));
+        assertEquals(true, exec("if ('foo' ==~ /foo/) { return true } else { return false }"));
     }
 
     public void testPatternAfterInfixBoolean() {
-        assertEquals(true, exec("return false || /foo/.matcher(\"foo\").matches()"));
-        assertEquals(true, exec("return true && /foo/.matcher(\"foo\").matches()"));
+        assertEquals(true, exec("return false || /foo/.matcher('foo').matches()"));
+        assertEquals(true, exec("return true && /foo/.matcher('foo').matches()"));
+        assertEquals(true, exec("return false || 'foo' ==~ /foo/"));
+        assertEquals(true, exec("return true && 'foo' ==~ /foo/"));
     }
 
     public void testPatternAfterUnaryNotBoolean() {
-        assertEquals(false, exec("return !/foo/.matcher(\"foo\").matches()"));
-        assertEquals(true, exec("return !/foo/.matcher(\"bar\").matches()"));
+        assertEquals(false, exec("return !/foo/.matcher('foo').matches()"));
+        assertEquals(true, exec("return !/foo/.matcher('bar').matches()"));
     }
 
     public void testInTernaryCondition()  {
-        assertEquals(true, exec("return /foo/.matcher(\"foo\").matches() ? true : false"));
-        assertEquals(1, exec("def i = 0; i += /foo/.matcher(\"foo\").matches() ? 1 : 1; return i"));
+        assertEquals(true, exec("return /foo/.matcher('foo').matches() ? true : false"));
+        assertEquals(1, exec("def i = 0; i += /foo/.matcher('foo').matches() ? 1 : 1; return i"));
+        assertEquals(true, exec("return 'foo' ==~ /foo/ ? true : false"));
+        assertEquals(1, exec("def i = 0; i += 'foo' ==~ /foo/ ? 1 : 1; return i"));
     }
 
     public void testInTernaryTrueArm()  {
-        assertEquals(true, exec("def i = true; return i ? /foo/.matcher(\"foo\").matches() : false"));
+        assertEquals(true, exec("def i = true; return i ? /foo/.matcher('foo').matches() : false"));
+        assertEquals(true, exec("def i = true; return i ? 'foo' ==~ /foo/ : false"));
     }
 
     public void testInTernaryFalseArm()  {
-        assertEquals(true, exec("def i = false; return i ? false : /foo/.matcher(\"foo\").matches()"));
+        assertEquals(true, exec("def i = false; return i ? false : 'foo' ==~ /foo/"));
     }
 
     public void testRegexInFunction() {
-        assertEquals(true, exec("boolean m(String s) {/foo/.matcher(s).matches()} m(\"foo\")"));
+        assertEquals(true, exec("boolean m(String s) {/foo/.matcher(s).matches()} m('foo')"));
+        assertEquals(true, exec("boolean m(String s) {s ==~ /foo/} m('foo')"));
     }
 
     public void testReturnRegexFromFunction() {
-        assertEquals(true, exec("Pattern m(boolean a) {a ? /foo/ : /bar/} m(true).matcher(\"foo\").matches()"));
+        assertEquals(true, exec("Pattern m(boolean a) {a ? /foo/ : /bar/} m(true).matcher('foo').matches()"));
+        assertEquals(true, exec("Pattern m(boolean a) {a ? /foo/ : /bar/} 'foo' ==~ m(true)"));
+        assertEquals(false, exec("Pattern m(boolean a) {a ? /foo/ : /bar/} m(false).matcher('foo').matches()"));
+        assertEquals(false, exec("Pattern m(boolean a) {a ? /foo/ : /bar/} 'foo' ==~ m(false)"));
+    }
+
+    public void testCallMatcherDirectly() {
+        assertEquals(true, exec("return /foo/.matcher('foo').matches()"));
+        assertEquals(false, exec("return /foo/.matcher('bar').matches()"));
     }
 
+    public void testFindInIf() {
+        assertEquals(true, exec("if ('fooasdfbasdf' =~ /foo/) {return true} else {return false}"));
+        assertEquals(true, exec("if ('1fooasdfbasdf' =~ /foo/) {return true} else {return false}"));
+        assertEquals(false, exec("if ('1f11ooasdfbasdf' =~ /foo/) {return true} else {return false}"));
+    }
+
+    public void testFindCastToBoolean() {
+        assertEquals(true, exec("return (boolean)('fooasdfbasdf' =~ /foo/)"));
+        assertEquals(true, exec("return (boolean)('111fooasdfbasdf' =~ /foo/)"));
+        assertEquals(false, exec("return (boolean)('fo11oasdfbasdf' =~ /foo/)"));
+    }
+
+    public void testFindOrStringConcat() {
+        assertEquals(true, exec("return 'f' + 'o' + 'o' =~ /foo/"));
+    }
+
+    public void testFindOfDef() {
+        assertEquals(true, exec("def s = 'foo'; return s =~ /foo/"));
+    }
+
+    public void testFindOnInput() {
+        assertEquals(true, exec("return params.s =~ /foo/", singletonMap("s", "fooasdfdf")));
+        assertEquals(false, exec("return params.s =~ /foo/", singletonMap("s", "11f2ooasdfdf")));
+    }
+
+    public void testGroup() {
+        assertEquals("foo", exec("Matcher m = /foo/.matcher('foo'); m.find(); return m.group()"));
+    }
+
+    public void testNumberedGroup() {
+        assertEquals("o", exec("Matcher m = /(f)(o)o/.matcher('foo'); m.find(); return m.group(2)"));
+    }
+
+    public void testNamedGroup() {
+        assertEquals("o", exec("Matcher m = /(?<first>f)(?<second>o)o/.matcher('foo'); m.find(); return m.namedGroup('second')"));
+    }
+
+    public void testCantUsePatternCompile() {
+        IllegalArgumentException e = expectScriptThrows(IllegalArgumentException.class, () -> {
+            exec("Pattern.compile('aa')");
+        });
+        assertEquals("Unknown call [compile] with [1] arguments on type [Pattern].", e.getMessage());
+    }
+
+    public void testBadRegexPattern() {
+        PatternSyntaxException e = expectScriptThrows(PatternSyntaxException.class, () -> {
+            exec("/\\ujjjj/"); // Invalid unicode
+        });
+        assertThat(e.getMessage(), containsString("Illegal Unicode escape sequence near index 2"));
+        assertThat(e.getMessage(), containsString("\\ujjjj"));
+    }
+
+    public void testRegexAgainstNumber() {
+        ClassCastException e = expectScriptThrows(ClassCastException.class, () -> {
+            exec("12 ==~ /cat/");
+        });
+        assertEquals("Cannot cast from [int] to [String].", e.getMessage());
+    }
 }

+ 0 - 16
modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java

@@ -22,7 +22,6 @@ package org.elasticsearch.painless;
 import java.lang.invoke.WrongMethodTypeException;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.regex.PatternSyntaxException;
 
 import static java.util.Collections.emptyMap;
 import static org.hamcrest.Matchers.containsString;
@@ -214,21 +213,6 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
         assertEquals("invalid sequence of tokens near ['}'].", e.getMessage());
     }
 
-    public void testCantUsePatternCompile() {
-        IllegalArgumentException e = expectScriptThrows(IllegalArgumentException.class, () -> {
-            exec("Pattern.compile(\"aa\")");
-        });
-        assertEquals("Unknown call [compile] with [1] arguments on type [Pattern].", e.getMessage());
-    }
-
-    public void testBadRegexPattern() {
-        PatternSyntaxException e = expectScriptThrows(PatternSyntaxException.class, () -> {
-            exec("/\\ujjjj/"); // Invalid unicode
-        });
-        assertThat(e.getMessage(), containsString("Illegal Unicode escape sequence near index 2"));
-        assertThat(e.getMessage(), containsString("\\ujjjj"));
-    }
-
     public void testBadBoxingCast() {
         expectScriptThrows(ClassCastException.class, () -> {
             exec("BitSet bs = new BitSet(); bs.and(2);");

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.