[jruby-joni] 160/279: Imported Upstream version 1.1.7+git20120721

Hideki Yamane henrich at moszumanska.debian.org
Mon Nov 16 11:27:22 UTC 2015


This is an automated email from the git hooks/post-receive script.

henrich pushed a commit to branch debian/sid
in repository jruby-joni.

commit 1974bf5c7e9fa576bbac00fc88bb6507c1fc3285
Author: Hideki Yamane <henrich at debian.org>
Date:   Sun Apr 7 09:12:45 2013 +0900

    Imported Upstream version 1.1.7+git20120721
---
 .gitignore                                         |    1 +
 .travis.yml                                        |    6 +
 MANIFEST.MF                                        |    2 +-
 pom.xml                                            |   14 +-
 src/org/joni/Analyser.java                         |  982 ++++++-------
 src/org/joni/ApplyCaseFold.java                    |   37 +-
 src/org/joni/ApplyCaseFoldArg.java                 |   24 +-
 src/org/joni/ArrayCompiler.java                    |  358 ++---
 src/org/joni/AsmCompiler.java                      |   26 +-
 src/org/joni/AsmCompilerSupport.java               |   44 +-
 src/org/joni/BitSet.java                           |   40 +-
 src/org/joni/BitStatus.java                        |   30 +-
 src/org/joni/ByteCodeMachine.java                  |  650 +++++----
 src/org/joni/ByteCodePrinter.java                  |  194 ++-
 src/org/joni/CaptureTreeNode.java                  |   36 +-
 src/org/joni/CodeRangeBuffer.java                  |  146 +-
 src/org/joni/Compiler.java                         |   66 +-
 src/org/joni/Config.java                           |   60 +-
 src/org/joni/Lexer.java                            | 1440 +++++++++-----------
 src/org/joni/Matcher.java                          |  157 ++-
 src/org/joni/MatcherFactory.java                   |   22 +-
 src/org/joni/MinMaxLen.java                        |   82 +-
 src/org/joni/NameEntry.java                        |   40 +-
 src/org/joni/NativeMachine.java                    |   22 +-
 src/org/joni/NodeOptInfo.java                      |   54 +-
 src/org/joni/OptAnchorInfo.java                    |   42 +-
 src/org/joni/OptEnvironment.java                   |   24 +-
 src/org/joni/OptExactInfo.java                     |  107 +-
 src/org/joni/OptMapInfo.java                       |   56 +-
 src/org/joni/Option.java                           |   80 +-
 src/org/joni/Parser.java                           |  492 +++----
 src/org/joni/Regex.java                            |  136 +-
 src/org/joni/Region.java                           |   36 +-
 src/org/joni/ScanEnvironment.java                  |   46 +-
 src/org/joni/ScannerSupport.java                   |   88 +-
 src/org/joni/SearchAlgorithm.java                  |  274 ++--
 src/org/joni/StackEntry.java                       |   44 +-
 src/org/joni/StackMachine.java                     |  146 +-
 src/org/joni/Syntax.java                           |  382 +++---
 src/org/joni/Token.java                            |   24 +-
 src/org/joni/UnsetAddrList.java                    |   34 +-
 src/org/joni/WarnCallback.java                     |   35 +-
 src/org/joni/{NativeMachine.java => Warnings.java} |   31 +-
 src/org/joni/ast/AnchorNode.java                   |   44 +-
 src/org/joni/ast/AnyCharNode.java                  |   32 +-
 src/org/joni/ast/BackRefNode.java                  |   54 +-
 src/org/joni/ast/CClassNode.java                   |  221 +--
 src/org/joni/ast/CTypeNode.java                    |   36 +-
 src/org/joni/ast/CallNode.java                     |   46 +-
 src/org/joni/ast/ConsAltNode.java                  |   65 +-
 src/org/joni/ast/EncloseNode.java                  |   76 +-
 src/org/joni/ast/Node.java                         |   73 +-
 src/org/joni/ast/QuantifierNode.java               |  106 +-
 src/org/joni/ast/StateNode.java                    |  154 +--
 src/org/joni/ast/StringNode.java                   |   98 +-
 src/org/joni/bench/AbstractBench.java              |    4 +-
 src/org/joni/constants/AnchorType.java             |   32 +-
 src/org/joni/constants/Arguments.java              |   26 +-
 src/org/joni/constants/AsmConstants.java           |   22 +-
 src/org/joni/constants/CCSTATE.java                |   22 +-
 src/org/joni/constants/CCVALTYPE.java              |   22 +-
 src/org/joni/constants/EncloseType.java            |   24 +-
 src/org/joni/constants/MetaChar.java               |   26 +-
 src/org/joni/constants/NodeStatus.java             |   22 +-
 src/org/joni/constants/NodeType.java               |   32 +-
 src/org/joni/constants/OPCode.java                 |   50 +-
 src/org/joni/constants/OPSize.java                 |   29 +-
 src/org/joni/constants/Reduce.java                 |   37 +-
 src/org/joni/constants/RegexState.java             |   22 +-
 src/org/joni/constants/StackPopLevel.java          |   22 +-
 src/org/joni/constants/StackType.java              |   22 +-
 src/org/joni/constants/StringType.java             |   22 +-
 src/org/joni/constants/SyntaxProperties.java       |   28 +-
 src/org/joni/constants/TargetInfo.java             |   22 +-
 src/org/joni/constants/TokenType.java              |   22 +-
 src/org/joni/constants/Traverse.java               |   22 +-
 src/org/joni/exception/ErrorMessages.java          |   30 +-
 src/org/joni/exception/InternalException.java      |   24 +-
 src/org/joni/exception/JOniException.java          |   22 +-
 src/org/joni/exception/SyntaxException.java        |   22 +-
 src/org/joni/exception/ValueException.java         |   28 +-
 test/org/joni/test/Test.java                       |   88 +-
 test/org/joni/test/TestA.java                      |   60 +-
 test/org/joni/test/TestC.java                      |   54 +-
 test/org/joni/test/TestCornerCases.java            |   28 +-
 test/org/joni/test/TestCrnl.java                   |   30 +-
 test/org/joni/test/TestJoni.java                   |   40 +-
 .../org/joni/test/TestLookBehind.java              |   69 +-
 test/org/joni/test/TestNSU8.java                   |   32 +-
 test/org/joni/test/TestU.java                      |  114 +-
 test/org/joni/test/TestU8.java                     |   84 ++
 91 files changed, 4588 insertions(+), 4282 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb5a316
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6768b88
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: java
+notifications:
+  # Email notifications are disabled to not annoy anybody.
+  # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more
+  # about configuring notification recipients and more.
+  email: false
diff --git a/MANIFEST.MF b/MANIFEST.MF
index ae40ba2..0dfec5c 100644
--- a/MANIFEST.MF
+++ b/MANIFEST.MF
@@ -1,2 +1,2 @@
 Implementation-Title: Joni (java port of Oniguruma)
-Implementation-Version: 1.1.1
+Implementation-Version: 1.1.7
diff --git a/pom.xml b/pom.xml
index 36af172..aca0e9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
   <groupId>org.jruby.joni</groupId>
   <artifactId>joni</artifactId>
   <packaging>jar</packaging>
-  <version>1.1.4</version>
+  <version>1.1.8</version>
   <name>Joni</name>
   <description>
     Java port of Oniguruma: http://www.geocities.jp/kosako3/oniguruma
@@ -18,9 +18,9 @@
   </issueManagement>
 
   <scm>
-    <connection>scm:svn:http://svn.codehaus.org/jruby</connection>
-    <developerConnection>scm:svn:https://svn.codehaus.org/jruby</developerConnection>
-    <url>http://svn.codehaus.org/jruby</url>
+    <connection>scm:git:https://github.com/jruby/joni.git</connection>
+    <developerConnection>scm:git:git at github.com:jruby/joni.git</developerConnection>
+    <url>https://github.com/jruby/joni</url>
   </scm>
 
   <licenses>
@@ -75,7 +75,7 @@
     <dependency>
        <groupId>org.jruby.jcodings</groupId>
        <artifactId>jcodings</artifactId>
-       <version>1.0.4</version>
+       <version>1.0.8</version>
     </dependency>
     <dependency>
       <groupId>junit</groupId>
@@ -84,9 +84,9 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>asm</groupId>
+      <groupId>org.ow2.asm</groupId>
       <artifactId>asm</artifactId>
-      <version>3.0</version>
+      <version>4.0</version>
       <scope>provided</scope>
     </dependency>
   </dependencies>
diff --git a/src/org/joni/Analyser.java b/src/org/joni/Analyser.java
index cce91ba..d66badf 100644
--- a/src/org/joni/Analyser.java
+++ b/src/org/joni/Analyser.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -35,6 +35,8 @@ import static org.joni.ast.QuantifierNode.isRepeatInfinite;
 import java.util.HashSet;
 
 import org.jcodings.CaseFoldCodeItem;
+import org.jcodings.ObjPtr;
+import org.jcodings.Ptr;
 import org.jcodings.constants.CharacterType;
 import org.joni.ast.AnchorNode;
 import org.joni.ast.BackRefNode;
@@ -54,18 +56,18 @@ import org.joni.constants.StackPopLevel;
 import org.joni.constants.TargetInfo;
 
 final class Analyser extends Parser {
-    
+
     protected Analyser(ScanEnvironment env, byte[]bytes, int p, int end) {
         super(env, bytes, p, end);
     }
-    
+
     protected final void compile() {
         regex.state = RegexState.COMPILING;
-        
+
         if (Config.DEBUG) {
             Config.log.println(regex.encStringToString(bytes, getBegin(), getEnd()));
         }
-        
+
         reset();
 
         regex.numMem = 0;
@@ -73,7 +75,7 @@ final class Analyser extends Parser {
         regex.numNullCheck = 0;
         //regex.repeatRangeAlloc = 0;
         regex.repeatRangeLo = null;
-        regex.repeatRangeHi = null;        
+        regex.repeatRangeHi = null;
         regex.numCombExpCheck = 0;
 
         if (Config.USE_COMBINATION_EXPLOSION_CHECK) regex.numCombExpCheck = 0;
@@ -83,19 +85,19 @@ final class Analyser extends Parser {
         if (Config.USE_NAMED_GROUP) {
             /* mixed use named group and no-named group */
             if (env.numNamed > 0 && syntax.captureOnlyNamedGroup() && !isCaptureGroup(regex.options)) {
-                if (env.numNamed != env.numMem) {                    
+                if (env.numNamed != env.numMem) {
                     root = disableNoNameGroupCapture(root);
                 } else {
                     numberedRefCheck(root);
                 }
             }
         } // USE_NAMED_GROUP
-       
+
         if (Config.USE_NAMED_GROUP) {
             if (env.numCall > 0) {
                 env.unsetAddrList = new UnsetAddrList(env.numCall);
                 setupSubExpCall(root);
-                // r != 0 ???                
+                // r != 0 ???
                 subexpRecursiveCheckTrav(root);
                 // r < 0 -< err, FOUND_CALLED_NODE = 1
                 subexpInfRecursiveCheckTrav(root);
@@ -105,28 +107,34 @@ final class Analyser extends Parser {
                 regex.numCall = 0;
             }
         } // USE_NAMED_GROUP
-        
-        setupTree(root, 0);        
+
+        if (Config.DEBUG_PARSE_TREE_RAW && Config.DEBUG_PARSE_TREE) {
+            Config.log.println("<RAW TREE>");
+            Config.log.println(root + "\n");
+        }
+
+        root = setupTree(root, 0);
         if (Config.DEBUG_PARSE_TREE) {
-            root.verifyTree(new HashSet<Node>(),env.reg.warnings);
+            if (Config.DEBUG_PARSE_TREE_RAW) Config.log.println("<TREE>");
+            root.verifyTree(new HashSet<Node>(), env.reg.warnings);
             Config.log.println(root + "\n");
         }
-        
+
         regex.captureHistory = env.captureHistory;
         regex.btMemStart = env.btMemStart;
         regex.btMemEnd = env.btMemEnd;
-        
+
         if (isFindCondition(regex.options)) {
             regex.btMemEnd = bsAll();
         } else {
             regex.btMemEnd = env.btMemEnd;
             regex.btMemEnd |= regex.captureHistory;
         }
-        
+
         if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
-            if (env.backrefedMem == 0 || (Config.USE_SUBEXP_CALL && env.numCall == 0)) {                
+            if (env.backrefedMem == 0 || (Config.USE_SUBEXP_CALL && env.numCall == 0)) {
                 setupCombExpCheck(root, 0);
-                
+
                 if (Config.USE_SUBEXP_CALL && env.hasRecursion) {
                     env.numCombExpCheck = 0;
                 } else { // USE_SUBEXP_CALL
@@ -139,17 +147,20 @@ final class Analyser extends Parser {
                         }
                     }
                 }
-                
+
             } // USE_SUBEXP_CALL
             regex.numCombExpCheck = env.numCombExpCheck;
         } // USE_COMBINATION_EXPLOSION_CHECK
-        
+
         regex.clearOptimizeInfo();
-        
+
         if (!Config.DONT_OPTIMIZE) setOptimizedInfoFromTree(root);
 
         env.memNodes = null;
-        
+
+        new ArrayCompiler(this).compile();
+        //new AsmCompiler(this).compile();
+
         if (regex.numRepeat != 0 || regex.btMemEnd != 0) {
             regex.stackPopLevel = StackPopLevel.ALL;
         } else {
@@ -160,66 +171,85 @@ final class Analyser extends Parser {
             }
         }
 
-        new ArrayCompiler(this).compile();
-        //new AsmCompiler(this).compile();
-
         if (Config.DEBUG_COMPILE) {
             if (Config.USE_NAMED_GROUP) Config.log.print(regex.nameTableToString());
             Config.log.println("stack used: " + regex.stackNeeded);
+            if (Config.USE_STRING_TEMPLATES) Config.log.print("templates: " + regex.templateNum + "\n");
             Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
+
         } // DEBUG_COMPILE
-        
+
         regex.state = RegexState.NORMAL;
     }
-    
-    private Node noNameDisableMap(Node node, int[]map, int[]counter) {
-        
+
+    private void noNameDisableMapFor_cosAlt(Node node, int[]map, Ptr counter) {
+        ConsAltNode can = (ConsAltNode)node;
+        do {
+            can.setCar(noNameDisableMap(can.car, map, counter));
+        } while ((can = can.cdr) != null);
+    }
+
+    private void noNameDisableMapFor_quantifier(Node node, int[]map, Ptr counter) {
+        QuantifierNode qn = (QuantifierNode)node;
+        Node target = qn.target;
+        Node old = target;
+        target = noNameDisableMap(target, map, counter);
+
+        if (target != old) {
+            qn.setTarget(target);
+            if (target.getType() == NodeType.QTFR) qn.reduceNestedQuantifier((QuantifierNode)target);
+        }
+    }
+
+    private Node noNameDisableMapFor_enclose(Node node, int[]map, Ptr counter) {
+        EncloseNode en = (EncloseNode)node;
+        if (en.type == EncloseType.MEMORY) {
+            if (en.isNamedGroup()) {
+                counter.p++;
+                map[en.regNum] = counter.p;
+                en.regNum = counter.p;
+                //en.target = noNameDisableMap(en.target, map, counter);
+                en.setTarget(noNameDisableMap(en.target, map, counter)); // ???
+            } else {
+                node = en.target;
+                en.target = null; // remove first enclose: /(a)(?<b>c)/
+                node = noNameDisableMap(node, map, counter);
+            }
+        } else {
+            //en.target = noNameDisableMap(en.target, map, counter);
+            en.setTarget(noNameDisableMap(en.target, map, counter)); // ???
+        }
+        return node;
+    }
+
+    private void noNameDisableMapFor_anchor(Node node, int[]map, Ptr counter) {
+        AnchorNode an = (AnchorNode)node;
+        switch (an.type) {
+            case AnchorNode.PREC_READ:
+            case AnchorNode.PREC_READ_NOT:
+            case AnchorNode.LOOK_BEHIND:
+            case AnchorNode.LOOK_BEHIND_NOT:
+                an.setTarget(noNameDisableMap(an.target, map, counter));
+        }
+    }
+
+    private Node noNameDisableMap(Node node, int[]map, Ptr counter) {
         switch (node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
-            ConsAltNode can = (ConsAltNode)node;
-            do {
-                can.setCar(noNameDisableMap(can.car, map, counter));                
-            } while ((can = can.cdr) != null);
+            noNameDisableMapFor_cosAlt(node, map, counter);
             break;
-            
         case NodeType.QTFR:
-            QuantifierNode qn = (QuantifierNode)node;
-            Node target = qn.target;
-            Node old = target;
-            target = noNameDisableMap(target, map, counter);
-            
-            if (target != old) {
-                qn.setTarget(target);
-                if (target.getType() == NodeType.QTFR) qn.reduceNestedQuantifier((QuantifierNode)target);
-            }
+            noNameDisableMapFor_quantifier(node, map, counter);
             break;
-            
         case NodeType.ENCLOSE:
-            EncloseNode en = (EncloseNode)node;
-            if (en.type == EncloseType.MEMORY) {
-                if (en.isNamedGroup()) {
-                    counter[0]++;
-                    map[en.regNum] = counter[0];
-                    en.regNum = counter[0];
-                    //en.target = noNameDisableMap(en.target, map, counter);
-                    en.setTarget(noNameDisableMap(en.target, map, counter)); // ???
-                } else {
-                    node = en.target;
-                    en.target = null; // remove first enclose: /(a)(?<b>c)/
-                    node = noNameDisableMap(node, map, counter);
-                }
-            } else {
-                //en.target = noNameDisableMap(en.target, map, counter);
-                en.setTarget(noNameDisableMap(en.target, map, counter)); // ???
-            }
+            node = noNameDisableMapFor_enclose(node, map, counter);
             break;
-            
-        default:
+        case NodeType.ANCHOR:
+            noNameDisableMapFor_anchor(node, map, counter);
             break;
         } // switch
-
-        return node;        
+        return node;
     }
 
     private void renumberByMap(Node node, int[]map) {
@@ -231,26 +261,22 @@ final class Analyser extends Parser {
                 renumberByMap(can.car, map);
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             renumberByMap(((QuantifierNode)node).target, map);
             break;
-            
+
         case NodeType.ENCLOSE:
             renumberByMap(((EncloseNode)node).target, map);
             break;
-            
+
         case NodeType.BREF:
             ((BackRefNode)node).renumber(map);
             break;
-            
-        default:
-            break;
         } // switch
     }
-    
+
     protected final void numberedRefCheck(Node node) {
-        
         switch (node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
@@ -259,58 +285,54 @@ final class Analyser extends Parser {
                 numberedRefCheck(can.car);
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             numberedRefCheck(((QuantifierNode)node).target);
             break;
-            
+
         case NodeType.ENCLOSE:
             numberedRefCheck(((EncloseNode)node).target);
             break;
-            
+
         case NodeType.BREF:
             BackRefNode br = (BackRefNode)node;
             if (!br.isNameRef()) newValueException(ERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED);
             break;
-            
-        default:
-            break;
         } // switch
     }
-    
+
     protected final Node disableNoNameGroupCapture(Node root) {
         int[]map = new int[env.numMem + 1];
-        
+
         for (int i=1; i<=env.numMem; i++) map[i] = 0;
-        
-        int[]counter = new int[]{0}; // !!! this should be passed as the recursion goes right ?, move to plain int
-        root = noNameDisableMap(root, map, counter); // ???
+
+        root = noNameDisableMap(root, map, new Ptr(0));
         renumberByMap(root, map);
-        
+
         for (int i=1, pos=1; i<=env.numMem; i++) {
             if (map[i] > 0) {
                 env.memNodes[pos] = env.memNodes[i];
                 pos++;
             }
         }
-        
+
         int loc = env.captureHistory;
         env.captureHistory = bsClear();
-        
+
         for (int i=1; i<=Config.MAX_CAPTURE_HISTORY_GROUP; i++) {
             if (bsAt(loc, i)) {
                 env.captureHistory = bsOnAtSimple(env.captureHistory, map[i]);
             }
         }
-        
+
         env.numMem = env.numNamed;
         regex.numMem = env.numNamed;
-        
+
         regex.renumberNameTable(map);
 
         return root;
     }
-    
+
     private void swap(Node a, Node b) {
         a.swap(b);
 
@@ -318,13 +340,13 @@ final class Analyser extends Parser {
             root = a;
         } else if (root == a) {
             root = b;
-        } 
+        }
     }
-    
+
     // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
     private int quantifiersMemoryInfo(Node node) {
         int info = 0;
-        
+
         switch(node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
@@ -334,7 +356,7 @@ final class Analyser extends Parser {
                 if (v > info) info = v;
             } while ((can = can.cdr) != null);
             break;
-        
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 CallNode cn = (CallNode)node;
@@ -343,33 +365,32 @@ final class Analyser extends Parser {
                 } else {
                     info = quantifiersMemoryInfo(cn.target);
                 }
-                break;
             } // USE_SUBEXP_CALL
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             if (qn.upper != 0) {
                 info = quantifiersMemoryInfo(qn.target);
             }
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             switch (en.type) {
             case EncloseType.MEMORY:
                 return TargetInfo.IS_EMPTY_MEM;
-                
+
             case EncloseType.OPTION:
             case EncloseNode.STOP_BACKTRACK:
                 info = quantifiersMemoryInfo(en.target);
                 break;
-                
+
             default:
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.BREF:
         case NodeType.STR:
         case NodeType.CTYPE:
@@ -379,28 +400,28 @@ final class Analyser extends Parser {
         default:
             break;
         } // switch
-        
+
         return info;
     }
-    
+
     private int getMinMatchLength(Node node) {
         int min = 0;
-        
+
         switch (node.getType()) {
         case NodeType.BREF:
             BackRefNode br = (BackRefNode)node;
             if (br.isRecursion()) break;
-            
+
             if (br.back[0] > env.numMem) newValueException(ERR_INVALID_BACKREF);
             min = getMinMatchLength(env.memNodes[br.back[0]]);
-            
-            for (int i=1; i<br.backNum; i++) { 
+
+            for (int i=1; i<br.backNum; i++) {
                 if (br.back[i] > env.numMem) newValueException(ERR_INVALID_BACKREF);
                 int tmin = getMinMatchLength(env.memNodes[br.back[i]]);
                 if (min > tmin) min = tmin;
             }
             break;
-            
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 CallNode cn = (CallNode)node;
@@ -410,18 +431,16 @@ final class Analyser extends Parser {
                 } else {
                     min = getMinMatchLength(cn.target);
                 }
-                break;
-                
             } // USE_SUBEXP_CALL
             break;
-            
+
         case NodeType.LIST:
             ConsAltNode can = (ConsAltNode)node;
             do {
                 min += getMinMatchLength(can.car);
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode y = (ConsAltNode)node;
             do {
@@ -434,20 +453,20 @@ final class Analyser extends Parser {
                 }
             } while ((y = y.cdr) != null);
             break;
-            
+
         case NodeType.STR:
             min = ((StringNode)node).length();
             break;
-            
+
         case NodeType.CTYPE:
             min = 1;
             break;
-            
+
         case NodeType.CCLASS:
         case NodeType.CANY:
             min = 1;
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             if (qn.lower > 0) {
@@ -455,7 +474,7 @@ final class Analyser extends Parser {
                 min = MinMaxLen.distanceMultiply(min, qn.lower);
             }
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             switch (en.type) {
@@ -468,28 +487,27 @@ final class Analyser extends Parser {
                         en.minLength = min;
                         en.setMinFixed();
                     }
-                    break;                    
                 } // USE_SUBEXP_CALL
                 break;
-                
+
             case EncloseType.OPTION:
             case EncloseType.STOP_BACKTRACK:
                 min = getMinMatchLength(en.target);
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.ANCHOR:
         default:
             break;
         } // switch
-        
+
         return min;
     }
-    
+
     private int getMaxMatchLength(Node node) {
         int max = 0;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
             ConsAltNode ln = (ConsAltNode)node;
@@ -498,7 +516,7 @@ final class Analyser extends Parser {
                 max = MinMaxLen.distanceAdd(max, tmax);
             } while ((ln = ln.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode an = (ConsAltNode)node;
             do {
@@ -506,34 +524,34 @@ final class Analyser extends Parser {
                 if (max < tmax) max = tmax;
             } while ((an = an.cdr) != null);
             break;
-            
+
         case NodeType.STR:
             max = ((StringNode)node).length();
             break;
-            
+
         case NodeType.CTYPE:
             max = enc.maxLengthDistance();
             break;
-            
+
         case NodeType.CCLASS:
         case NodeType.CANY:
             max = enc.maxLengthDistance();
             break;
-            
+
         case NodeType.BREF:
             BackRefNode br = (BackRefNode)node;
-            if (br.isRecursion()) {            
+            if (br.isRecursion()) {
                 max = MinMaxLen.INFINITE_DISTANCE;
                 break;
             }
-            
+
             for (int i=0; i<br.backNum; i++) {
                 if (br.back[i] > env.numMem) newValueException(ERR_INVALID_BACKREF);
                 int tmax = getMaxMatchLength(env.memNodes[br.back[i]]);
                 if (max < tmax) max = tmax;
             }
             break;
-            
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 CallNode cn = (CallNode)node;
@@ -542,24 +560,23 @@ final class Analyser extends Parser {
                 } else {
                     max = MinMaxLen.INFINITE_DISTANCE;
                 }
-                break;
             } // USE_SUBEXP_CALL
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             if (qn.upper != 0) {
                 max = getMaxMatchLength(qn.target);
                 if (max != 0) {
                     if (!isRepeatInfinite(qn.upper)) {
-                        max = MinMaxLen.distanceMultiply(max, qn.upper); 
+                        max = MinMaxLen.distanceMultiply(max, qn.upper);
                     } else {
                         max = MinMaxLen.INFINITE_DISTANCE;
                     }
                 }
             }
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             switch (en.type) {
@@ -572,34 +589,33 @@ final class Analyser extends Parser {
                         en.maxLength = max;
                         en.setMaxFixed();
                     }
-                    break;
                 } // USE_SUBEXP_CALL
                 break;
-            
+
             case EncloseType.OPTION:
             case EncloseType.STOP_BACKTRACK:
                 max = getMaxMatchLength(en.target);
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.ANCHOR:
         default:
             break;
         } // switch
-        
+
         return max;
     }
-    
+
     private static final int GET_CHAR_LEN_VARLEN            = -1;
     private static final int GET_CHAR_LEN_TOP_ALT_VARLEN    = -2;
     protected final int getCharLengthTree(Node node) {
         return getCharLengthTree(node, 0);
     }
-    
+
     private int getCharLengthTree(Node node, int level) {
         level++;
-        
+
         int len = 0;
         returnCode = 0;
 
@@ -611,11 +627,11 @@ final class Analyser extends Parser {
                 if (returnCode == 0) len = MinMaxLen.distanceAdd(len, tlen);
             } while (returnCode == 0 && (ln = ln.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode an = (ConsAltNode)node;
             boolean varLen = false;
-            
+
             int tlen = getCharLengthTree(an.car, level);
             while (returnCode == 0 && (an = an.cdr) != null) {
                 int tlen2 = getCharLengthTree(an.car, level);
@@ -636,34 +652,33 @@ final class Analyser extends Parser {
                 }
             }
             break;
-            
+
         case NodeType.STR:
             StringNode sn = (StringNode)node;
             len = sn.length(enc);
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             if (qn.lower == qn.upper) {
                 tlen = getCharLengthTree(qn.target, level);
-                if (returnCode == 0) len = MinMaxLen.distanceMultiply(tlen, qn.lower);                
+                if (returnCode == 0) len = MinMaxLen.distanceMultiply(tlen, qn.lower);
             } else {
                 returnCode = GET_CHAR_LEN_VARLEN;
             }
             break;
-            
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 CallNode cn = (CallNode)node;
                 if (!cn.isRecursion()) {
-                    len = getCharLengthTree(cn.target, level); 
+                    len = getCharLengthTree(cn.target, level);
                 } else {
                     returnCode = GET_CHAR_LEN_VARLEN;
                 }
-                break;
             } // USE_SUBEXP_CALL
             break;
-            
+
         case NodeType.CTYPE:
             len = 1;
 
@@ -671,7 +686,7 @@ final class Analyser extends Parser {
         case NodeType.CANY:
             len = 1;
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             switch(en.type) {
@@ -686,10 +701,9 @@ final class Analyser extends Parser {
                             en.setCLenFixed();
                         }
                     }
-                    break;
                 } // USE_SUBEXP_CALL
                 break;
-                
+
             case EncloseType.OPTION:
             case EncloseType.STOP_BACKTRACK:
                 len = getCharLengthTree(en.target, level);
@@ -699,20 +713,20 @@ final class Analyser extends Parser {
 
         case NodeType.ANCHOR:
             break;
-            
+
         default:
             returnCode = GET_CHAR_LEN_VARLEN;
         } // switch
         return len;
     }
-    
+
     /* x is not included y ==>  1 : 0 */
     private boolean isNotIncluded(Node x, Node y) {
         Node tmp;
 
         // !retry:!
-        retry:while(true) {
-            
+        retry: while(true) {
+
         int yType = y.getType();
 
         switch(x.getType()) {
@@ -722,30 +736,30 @@ final class Analyser extends Parser {
                 CTypeNode cny = (CTypeNode)y;
                 CTypeNode cnx = (CTypeNode)x;
                 return cny.ctype == cnx.ctype && cny.not != cnx.not;
-                
+
             case NodeType.CCLASS:
                 // !swap:!
                 tmp = x;
                 x = y;
                 y = tmp;
                 // !goto retry;!
-                continue retry;                
-                
+                continue retry;
+
             case NodeType.STR:
                 // !goto swap;!
                 tmp = x;
                 x = y;
                 y = tmp;
                 continue retry;
-                
+
             default:
                 break;
             } // inner switch
             break;
-          
+
         case NodeType.CCLASS:
             CClassNode xc = (CClassNode)x;
-            
+
             switch(yType) {
             case NodeType.CTYPE:
                 switch(((CTypeNode)y).ctype) {
@@ -754,7 +768,7 @@ final class Analyser extends Parser {
                         if (xc.mbuf == null && !xc.isNot()) {
                             for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
                                 if (xc.bs.at(i)) {
-                                    if (enc.isSbWord(i)) return false; 
+                                    if (enc.isSbWord(i)) return false;
                                 }
                             }
                             return true;
@@ -773,17 +787,17 @@ final class Analyser extends Parser {
                         return true;
                     }
                     // break; not reached
-                    
+
                 default:
-                    break; 
+                    break;
                 } // inner switch
                 break;
-                
+
             case NodeType.CCLASS:
                 CClassNode yc = (CClassNode)y;
-                
+
                 for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
-                    boolean v = xc.bs.at(i); 
+                    boolean v = xc.bs.at(i);
                     if ((v && !xc.isNot()) || (!v && xc.isNot())) {
                         v = yc.bs.at(i);
                         if ((v && !yc.isNot()) || (!v && yc.isNot())) return false;
@@ -792,7 +806,7 @@ final class Analyser extends Parser {
                 if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) return true;
                 return false;
                 // break; not reached
-                
+
             case NodeType.STR:
                 // !goto swap;!
                 tmp = x;
@@ -802,42 +816,42 @@ final class Analyser extends Parser {
 
             default:
                 break;
-            
+
             } // inner switch
             break; // case NodeType.CCLASS
-            
+
         case NodeType.STR:
             StringNode xs = (StringNode)x;
             if (xs.length() == 0) break;
-            
+
             switch (yType) {
             case NodeType.CTYPE:
                 CTypeNode cy = ((CTypeNode)y);
                 switch (cy.ctype) {
                 case CharacterType.WORD:
                     if (enc.isMbcWord(xs.bytes, xs.p, xs.end)) {
-                        return cy.not; 
+                        return cy.not;
                     } else {
                         return !cy.not;
                     }
-                    
+
                 default:
                     break;
-                    
+
                 } // inner switch
                 break;
-                
+
             case NodeType.CCLASS:
                 CClassNode cc = (CClassNode)y;
                 int code = enc.mbcToCode(xs.bytes, xs.p, xs.p + enc.maxLength());
                 return !cc.isCodeInCC(enc, code);
-                
+
             case NodeType.STR:
                 StringNode ys = (StringNode)y;
                 int len = xs.length();
                 if (len > ys.length()) len = ys.length();
                 if (xs.isAmbig() || ys.isAmbig()) {
-                    /* tiny version */                    
+                    /* tiny version */
                     return false;
                 } else {
                     for (int i=0, p=ys.p, q=xs.p; i<len; i++, p++, q++) {
@@ -845,52 +859,52 @@ final class Analyser extends Parser {
                     }
                 }
                 break;
-                
+
             default:
                 break;
             } // inner switch
-        
+
             break; // case NodeType.STR
-            
+
         } // switch
-        
+
         break;
-        } // retry:while
+        } // retry: while
         return false;
     }
-    
+
     private Node getHeadValueNode(Node node, boolean exact) {
         Node n = null;
-        
+
         switch(node.getType()) {
         case NodeType.BREF:
         case NodeType.ALT:
         case NodeType.CANY:
             break;
-            
+
         case NodeType.CALL:
-            break; // if (Config.USE_SUBEXP_CALL) 
-            
+            break; // if (Config.USE_SUBEXP_CALL)
+
         case NodeType.CTYPE:
         case NodeType.CCLASS:
             if (!exact) n = node;
             break;
-            
+
         case NodeType.LIST:
             n = getHeadValueNode(((ConsAltNode)node).car, exact);
             break;
-            
+
         case NodeType.STR:
             StringNode sn = (StringNode)node;
             if (sn.end <= sn.p) break; // ???
-            
+
             if (exact && !sn.isRaw() && isIgnoreCase(regex.options)){
                 // nothing
             } else {
                 n = node;
             }
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             if (qn.lower > 0) {
@@ -901,10 +915,10 @@ final class Analyser extends Parser {
                 }
             }
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
-            
+
             switch (en.type) {
             case EncloseType.OPTION:
                 int options = regex.options;
@@ -912,32 +926,32 @@ final class Analyser extends Parser {
                 n = getHeadValueNode(en.target, exact);
                 regex.options = options;
                 break;
-                
+
             case EncloseType.MEMORY:
             case EncloseType.STOP_BACKTRACK:
                 n = getHeadValueNode(en.target, exact);
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             if (an.type == AnchorType.PREC_READ) n = getHeadValueNode(an.target, exact);
             break;
-            
+
         default:
             break;
         } // switch
-        
+
         return n;
     }
-    
+
     // true: invalid
     private boolean checkTypeTree(Node node, int typeMask, int encloseMask, int anchorMask) {
         if ((node.getType2Bit() & typeMask) == 0) return true;
 
         boolean invalid = false;
-        
+
         switch(node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
@@ -946,37 +960,37 @@ final class Analyser extends Parser {
                 invalid = checkTypeTree(can.car, typeMask, encloseMask, anchorMask);
             } while (!invalid && (can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             invalid = checkTypeTree(((QuantifierNode)node).target, typeMask, encloseMask, anchorMask);
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             if ((en.type & encloseMask) == 0) return true;
             invalid = checkTypeTree(en.target, typeMask, encloseMask, anchorMask);
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             if ((an.type & anchorMask) == 0) return true;
-            
+
             if (an.target != null) invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
             break;
-            
+
         default:
             break;
-            
+
         } // switch
 
         return invalid;
     }
-    
+
     private static final int RECURSION_EXIST       = 1;
     private static final int RECURSION_INFINITE    = 2;
     private int subexpInfRecursiveCheck(Node node, boolean head) {
         int r = 0;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
             int min;
@@ -991,7 +1005,7 @@ final class Analyser extends Parser {
                 }
             } while ((x = x.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode can = (ConsAltNode)node;
             r = RECURSION_EXIST;
@@ -1001,7 +1015,7 @@ final class Analyser extends Parser {
                 r &= ret;
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             r = subexpInfRecursiveCheck(qn.target, head);
@@ -1009,7 +1023,7 @@ final class Analyser extends Parser {
                 if (qn.lower == 0) r = 0;
             }
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
@@ -1021,11 +1035,11 @@ final class Analyser extends Parser {
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.CALL:
             r = subexpInfRecursiveCheck(((CallNode)node).target, head);
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             if (en.isMark2()) {
@@ -1039,16 +1053,16 @@ final class Analyser extends Parser {
                 en.clearMark2();
             }
             break;
-            
+
         default:
             break;
         } // switch
         return r;
     }
-    
+
     protected final int subexpInfRecursiveCheckTrav(Node node) {
         int r = 0;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
@@ -1057,11 +1071,11 @@ final class Analyser extends Parser {
                 r = subexpInfRecursiveCheckTrav(can.car);
             } while (r == 0 && (can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             r = subexpInfRecursiveCheckTrav(((QuantifierNode)node).target);
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
@@ -1073,7 +1087,7 @@ final class Analyser extends Parser {
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             if (en.isRecursion()) {
@@ -1084,17 +1098,17 @@ final class Analyser extends Parser {
             }
             r = subexpInfRecursiveCheckTrav(en.target);
             break;
-            
+
         default:
             break;
         } // switch
-        
+
         return r;
     }
-    
+
     private int subexpRecursiveCheck(Node node) {
         int r = 0;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
@@ -1103,11 +1117,11 @@ final class Analyser extends Parser {
                 r |= subexpRecursiveCheck(can.car);
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             r = subexpRecursiveCheck(((QuantifierNode)node).target);
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
@@ -1119,13 +1133,13 @@ final class Analyser extends Parser {
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.CALL:
             CallNode cn = (CallNode)node;
             r = subexpRecursiveCheck(cn.target);
-            if (r != 0) cn.setRecursion(); 
+            if (r != 0) cn.setRecursion();
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             if (en.isMark2()) {
@@ -1138,18 +1152,18 @@ final class Analyser extends Parser {
                 en.clearMark2();
             }
             break;
-            
+
         default:
             break;
         } // switch
-        
+
         return r;
     }
-    
+
     private static final int FOUND_CALLED_NODE  = 1;
     protected final int subexpRecursiveCheckTrav(Node node) {
         int r = 0;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
         case NodeType.ALT:
@@ -1158,11 +1172,11 @@ final class Analyser extends Parser {
                 int ret = subexpRecursiveCheckTrav(can.car);
                 if (ret == FOUND_CALLED_NODE) {
                     r = FOUND_CALLED_NODE;
-                } 
+                }
                 // else if (ret < 0) return ret; ???
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             r = subexpRecursiveCheckTrav(qn.target);
@@ -1170,7 +1184,7 @@ final class Analyser extends Parser {
                 if (r == FOUND_CALLED_NODE) qn.isRefered = true;
             }
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
@@ -1182,7 +1196,7 @@ final class Analyser extends Parser {
                 break;
             } // inner switch
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             if (!en.isRecursion()) {
@@ -1196,16 +1210,25 @@ final class Analyser extends Parser {
             r = subexpRecursiveCheckTrav(en.target);
             if (en.isCalled()) r |= FOUND_CALLED_NODE;
             break;
-            
+
         default:
             break;
         } // switch
-        
+
         return r;
     }
-    
+
+    private void setCallAttr(CallNode cn) {
+        cn.target = env.memNodes[cn.groupNum]; // no setTarget in call nodes!
+        if (cn.target == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, cn.nameP, cn.nameEnd);
+
+        ((EncloseNode)cn.target).setCalled();
+        env.btMemStart = BitStatus.bsOnAt(env.btMemStart, cn.groupNum);
+        cn.unsetAddrList = env.unsetAddrList;
+    }
+
     protected final void setupSubExpCall(Node node) {
-        
+
         switch(node.getType()) {
         case NodeType.LIST:
             ConsAltNode ln = (ConsAltNode)node;
@@ -1213,25 +1236,25 @@ final class Analyser extends Parser {
                 setupSubExpCall(ln.car);
             } while ((ln = ln.cdr) != null);
             break;
-        
+
         case NodeType.ALT:
             ConsAltNode can = (ConsAltNode)node;
             do {
                 setupSubExpCall(can.car);
             } while ((can = can.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             setupSubExpCall(((QuantifierNode)node).target);
             break;
-            
+
         case NodeType.ENCLOSE:
             setupSubExpCall(((EncloseNode)node).target);
             break;
-            
+
         case NodeType.CALL:
             CallNode cn = (CallNode)node;
-            
+
             if (cn.groupNum != 0) {
                 int gNum = cn.groupNum;
 
@@ -1241,36 +1264,23 @@ final class Analyser extends Parser {
                     }
                 } // USE_NAMED_GROUP
                 if (gNum > env.numMem) newValueException(ERR_UNDEFINED_GROUP_REFERENCE, cn.nameP, cn.nameEnd);
-
-                // !goto set_call_attr!; // remove duplication ?
-                cn.target = env.memNodes[cn.groupNum]; // no setTarget in call nodes!
-                if (cn.target == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, cn.nameP, cn.nameEnd);
-
-                ((EncloseNode)cn.target).setCalled();
-                env.btMemStart = BitStatus.bsOnAt(env.btMemStart, cn.groupNum);
-                cn.unsetAddrList = env.unsetAddrList;
-            } else {            
+                setCallAttr(cn);
+            } else {
                 if (Config.USE_NAMED_GROUP) {
                     NameEntry ne = regex.nameToGroupNumbers(cn.name, cn.nameP, cn.nameEnd);
-                    
+
                     if (ne == null) {
                         newValueException(ERR_UNDEFINED_NAME_REFERENCE, cn.nameP, cn.nameEnd);
                     } else if (ne.backNum > 1) {
                         newValueException(ERR_MULTIPLEX_DEFINITION_NAME_CALL, cn.nameP, cn.nameEnd);
                     } else {
                         cn.groupNum = ne.backRef1; // ne.backNum == 1 ? ne.backRef1 : ne.backRefs[0]; // ??? need to check ?
-                        // !set_call_attr:!
-                        cn.target = env.memNodes[cn.groupNum]; // no setTarget in call nodes!
-                        if (cn.target == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, cn.nameP, cn.nameEnd);
-                        
-                        ((EncloseNode)cn.target).setCalled();
-                        env.btMemStart = BitStatus.bsOnAt(env.btMemStart, cn.groupNum);
-                        cn.unsetAddrList = env.unsetAddrList;
+                        setCallAttr(cn);
                     }
                 }
             }
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
@@ -1285,21 +1295,19 @@ final class Analyser extends Parser {
 
         } // switch
     }
-    
+
     /* divide different length alternatives in look-behind.
     (?<=A|B) ==> (?<=A)|(?<=B)
     (?<!A|B) ==> (?<!A)(?<!B)
      */
-    private void divideLookBehindAlternatives(Node node) {
+    private Node divideLookBehindAlternatives(Node node) {
         AnchorNode an = (AnchorNode)node;
         int anchorType = an.type;
-        
         Node head = an.target;
         Node np = ((ConsAltNode)head).car;
 
-        
         swap(node, head);
-        
+
         Node tmp = node;
         node = head;
         head = tmp;
@@ -1313,18 +1321,19 @@ final class Analyser extends Parser {
             insert.setTarget(((ConsAltNode)np).car);
             ((ConsAltNode)np).setCar(insert);
         }
-        
+
         if (anchorType == AnchorType.LOOK_BEHIND_NOT) {
             np = node;
             do {
                 ((ConsAltNode)np).toListNode(); /* alt -> list */
-            } while ((np = ((ConsAltNode)np).cdr) != null); 
+            } while ((np = ((ConsAltNode)np).cdr) != null);
         }
+
+        return node;
     }
-    
-    private void setupLookBehind(Node node) {
+
+    private Node setupLookBehind(Node node) {
         AnchorNode an = (AnchorNode)node;
-        
         int len = getCharLengthTree(an.target);
         switch(returnCode) {
         case 0:
@@ -1335,17 +1344,18 @@ final class Analyser extends Parser {
             break;
         case GET_CHAR_LEN_TOP_ALT_VARLEN:
             if (syntax.differentLengthAltLookBehind()) {
-                divideLookBehindAlternatives(node);
+                return divideLookBehindAlternatives(node);
             } else {
                 newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
             }
         }
+        return node;
     }
 
     private void nextSetup(Node node, Node nextNode) {
         // retry:
         retry: while(true) {
-        
+
         int type = node.getType();
         if (type == NodeType.QTFR) {
             QuantifierNode qn = (QuantifierNode)node;
@@ -1368,7 +1378,7 @@ final class Analyser extends Parser {
                                 en.setStopBtSimpleRepeat();
                                 //en.setTarget(qn.target); // optimize it ??
                                 swap(node, en);
-                                
+
                                 en.setTarget(node);
                             }
                         }
@@ -1383,89 +1393,128 @@ final class Analyser extends Parser {
                 continue retry;
             }
         }
-        
+
         break;
         } // while
     }
-    
-    private void updateStringNodeCaseFold(Node node) {
-        StringNode sn = (StringNode)node;
-        
-        byte[]sbuf = new byte[sn.length() << 1];
+
+    private void updateStringNodeCaseFoldSingleByte(StringNode sn, byte[]toLower) {
+        int end = sn.end;
+        byte[]bytes = sn.bytes;
         int sp = 0;
-        
-        value = sn.p;
+        int p = sn.p;
+
+        while (p < end) {
+            byte lower = toLower[bytes[p] & 0xff];
+            if (lower != bytes[p]) {
+                byte[]sbuf = new byte[end - sn.p];
+                System.arraycopy(bytes, sn.p, sbuf, 0, sp);
+
+                while (p < end) sbuf[sp++] = toLower[bytes[p++] & 0xff];
+
+                sn.set(sbuf, 0, sp);
+                break;
+            } else {
+                sp++;
+                p++;
+            }
+        }
+    }
+
+    private void updateStringNodeCaseFoldMultiByte(StringNode sn) {
         int end = sn.end;
-        
+        value = sn.p;
+        int sp = 0;
         byte[]buf = new byte[Config.ENC_MBC_CASE_FOLD_MAXLEN];
+
         while (value < end) {
-            int len = enc.mbcCaseFold(regex.caseFoldFlag, sn.bytes, this, end, buf);
-            for (int i=0; i<len; i++) {
-                if (sp >= sbuf.length) {
-                    byte[]tmp = new byte[sbuf.length << 1];
-                    System.arraycopy(sbuf, 0, tmp, 0, sbuf.length);
-                    sbuf = tmp;
+            int ovalue = value;
+            int len = enc.mbcCaseFold(regex.caseFoldFlag, bytes, this, end, buf);
+
+            for (int i = 0; i < len; i++) {
+                if (bytes[ovalue + i] != buf[i]) {
+
+                    byte[]sbuf = new byte[sn.length() << 1];
+                    System.arraycopy(bytes, sn.p, sbuf, 0, ovalue - sn.p);
+                    value = ovalue;
+                    while (value < end) {
+                        len = enc.mbcCaseFold(regex.caseFoldFlag, bytes, this, end, buf);
+                        for (i = 0; i < len; i++) {
+                            if (sp >= sbuf.length) {
+                                byte[]tmp = new byte[sbuf.length << 1];
+                                System.arraycopy(sbuf, 0, tmp, 0, sbuf.length);
+                                sbuf = tmp;
+                            }
+                            sbuf[sp++] = buf[i];
+                        }
+                    }
+                    sn.set(sbuf, 0, sp);
+                    return;
                 }
-                sbuf[sp++] = buf[i];
+                sp++;
             }
         }
-        
-        sn.set(sbuf, 0, sp);
     }
-    
+
+    private void updateStringNodeCaseFold(Node node) {
+        StringNode sn = (StringNode)node;
+        byte[] toLower = enc.toLowerCaseTable();
+        if (toLower != null) {
+            updateStringNodeCaseFoldSingleByte(sn, toLower);
+        } else {
+            updateStringNodeCaseFoldMultiByte(sn);
+        }
+    }
+
     private Node expandCaseFoldMakeRemString(byte[]bytes, int p, int end) {
         StringNode node = new StringNode(bytes, p, end);
-        
+
         updateStringNodeCaseFold(node);
         node.setAmbig();
         node.setDontGetOptInfo();
         return node;
     }
-    
+
     private boolean expandCaseFoldStringAlt(int itemNum, CaseFoldCodeItem[]items,
-                                              byte[]bytes, int p, int slen, int end, Node[]node) {
+                                              byte[]bytes, int p, int slen, int end, ObjPtr<Node> node) {
         boolean varlen = false;
-        
         for (int i=0; i<itemNum; i++) {
             if (items[i].byteLen != slen) {
                 varlen = true;
                 break;
             }
         }
-        
-        ConsAltNode varANode = null, anode, xnode;
+
+        ConsAltNode varANode = null, altNode, listNode;
         if (varlen) {
-            node[0] = varANode = newAltNode(null, null);
-            
-            xnode = newListNode(null, null);
-            varANode.setCar(xnode);
-            
-            anode = newAltNode(null, null);
-            xnode.setCar(anode);
+            node.p = varANode = newAltNode(null, null);
+
+            listNode = newListNode(null, null);
+            varANode.setCar(listNode);
+
+            altNode = newAltNode(null, null);
+            listNode.setCar(altNode);
         } else {
-            node[0] = anode = newAltNode(null, null);
+            node.p = altNode = newAltNode(null, null);
         }
-        
+
         StringNode snode = new StringNode(bytes, p, p + slen);
-        anode.setCar(snode);
-        
+        altNode.setCar(snode);
+
         for (int i=0; i<itemNum; i++) {
             snode = new StringNode();
-            
-            for (int j=0; j<items[i].codeLen; j++) {
-                snode.ensure(Config.ENC_CODE_TO_MBC_MAXLEN);
-                snode.end += enc.codeToMbc(items[i].code[j], snode.bytes, snode.end);
-            }
-            
+
+            for (int j = 0; j < items[i].codeLen; j++) snode.catCode(items[i].code[j], enc);
+
             ConsAltNode an = newAltNode(null, null);
             if (items[i].byteLen != slen) {
                 int q = p + items[i].byteLen;
                 if (q < end) {
                     Node rem = expandCaseFoldMakeRemString(bytes, q, end);
-                    
-                    xnode = ConsAltNode.listAdd(null, snode);
-                    ConsAltNode.listAdd(xnode, rem);
-                    an.setCar(xnode);
+
+                    listNode = ConsAltNode.listAdd(null, snode);
+                    ConsAltNode.listAdd(listNode, rem);
+                    an.setCar(listNode);
                 } else {
                     an.setCar(snode);
                 }
@@ -1473,18 +1522,18 @@ final class Analyser extends Parser {
                 varANode = an;
             } else {
                 an.setCar(snode);
-                anode.setCdr(an);
-                anode = an;
+                altNode.setCdr(an);
+                altNode = an;
             }
         }
         return varlen;
     }
-    
+
     private static final int THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION = 8;
-    private void expandCaseFoldString(Node node) {
+    private Node expandCaseFoldString(Node node) {
         StringNode sn = (StringNode)node;
 
-        if (sn.isAmbig() || sn.length() <= 0) return;
+        if (sn.isAmbig() || sn.length() <= 0) return node;
 
         byte[]bytes = sn.bytes;
         int p = sn.p;
@@ -1492,94 +1541,91 @@ final class Analyser extends Parser {
         int altNum = 1;
 
         ConsAltNode topRoot = null, root = null;
-        Node[]prevNode = new Node[]{null};
-        StringNode snode = null;
+        ObjPtr<Node> prevNode = new ObjPtr<Node>();
+        StringNode stringNode = null;
 
         while (p < end) {
             CaseFoldCodeItem[]items = enc.caseFoldCodesByString(regex.caseFoldFlag, bytes, p, end);
             int len = enc.length(bytes, p, end);
 
             if (items.length == 0) {
-                if (snode == null) {
-                    if (root == null && prevNode[0] != null) {
-                        topRoot = root = ConsAltNode.listAdd(null, prevNode[0]);
-                    }
-                    
-                    prevNode[0] = snode = new StringNode(); // onig_node_new_str(NULL, NULL);
-                    
-                    if (root != null) {
-                        ConsAltNode.listAdd(root, snode);
+                if (stringNode == null) {
+                    if (root == null && prevNode.p != null) {
+                        topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
                     }
-                    
+
+                    prevNode.p = stringNode = new StringNode(); // onig_node_new_str(NULL, NULL);
+
+                    if (root != null) ConsAltNode.listAdd(root, stringNode);
+
                 }
-            
-                snode.cat(bytes, p, p + len);
+
+                stringNode.cat(bytes, p, p + len);
             } else {
                 altNum *= (items.length + 1);
                 if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) break;
-                
-                if (root == null && prevNode[0] != null) {
-                    topRoot = root = ConsAltNode.listAdd(null, prevNode[0]);
+
+                if (root == null && prevNode.p != null) {
+                    topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
                 }
-                
-                boolean r = expandCaseFoldStringAlt(items.length, items, bytes, p, len, end, prevNode);
-                if (r) { // if (r == 1)
+
+                if (expandCaseFoldStringAlt(items.length, items, bytes, p, len, end, prevNode)) { // if (r == 1)
                     if (root == null) {
-                        topRoot = (ConsAltNode)prevNode[0];
+                        topRoot = (ConsAltNode)prevNode.p;
                     } else {
-                        ConsAltNode.listAdd(root, prevNode[0]);
+                        ConsAltNode.listAdd(root, prevNode.p);
                     }
-                    
-                    root = (ConsAltNode)((ConsAltNode)prevNode[0]).car;
+
+                    root = (ConsAltNode)((ConsAltNode)prevNode.p).car;
                 } else { /* r == 0 */
-                    if (root != null) {
-                        ConsAltNode.listAdd(root, prevNode[0]);
-                    }
+                    if (root != null) ConsAltNode.listAdd(root, prevNode.p);
                 }
-                snode = null;
+                stringNode = null;
             }
             p += len;
         }
-        
+
         if (p < end) {
             Node srem = expandCaseFoldMakeRemString(bytes, p, end);
-            
-            if (prevNode[0] != null && root == null) {
-                topRoot = root = ConsAltNode.listAdd(null, prevNode[0]);
+
+            if (prevNode.p != null && root == null) {
+                topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
             }
-            
+
             if (root == null) {
-                prevNode[0] = srem;
+                prevNode.p = srem;
             } else {
                 ConsAltNode.listAdd(root, srem);
             }
         }
         /* ending */
-        Node xnode = topRoot != null ? topRoot : prevNode[0];
+        Node xnode = topRoot != null ? topRoot : prevNode.p;
+
         swap(node, xnode);
+        return xnode;
     }
-    
+
     private static final int CEC_THRES_NUM_BIG_REPEAT       = 512;
     private static final int CEC_INFINITE_NUM               = 0x7fffffff;
 
     private static final int CEC_IN_INFINITE_REPEAT         = (1<<0);
     private static final int CEC_IN_FINITE_REPEAT           = (1<<1);
     private static final int CEC_CONT_BIG_REPEAT            = (1<<2);
-    
+
     protected final int setupCombExpCheck(Node node, int state) {
         int r = state;
         int ret;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
             ConsAltNode ln = (ConsAltNode)node;
-            
+
             do {
                 r = setupCombExpCheck(ln.car, r);
                 //prev = ((ConsAltNode)node).car;
             } while (r >= 0 && (ln = ln.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode an = (ConsAltNode)node;
             do {
@@ -1587,18 +1633,18 @@ final class Analyser extends Parser {
                 r |= ret;
             } while (ret >= 0 && (an = an.cdr) != null);
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             int childState = state;
             int addState = 0;
             int varNum;
-            
+
             if (!isRepeatInfinite(qn.upper)) {
                 if (qn.upper > 1) {
                     /* {0,1}, {1,1} are allowed */
                     childState |= CEC_IN_FINITE_REPEAT;
-                    
+
                     /* check (a*){n,m}, (a+){n,m} => (a*){n,n}, (a+){n,n} */
                     if (env.backrefedMem == 0) {
                         if (qn.target.getType() == NodeType.ENCLOSE) {
@@ -1616,7 +1662,7 @@ final class Analyser extends Parser {
                     }
                 }
             }
-            
+
             if ((state & CEC_IN_FINITE_REPEAT) != 0) {
                 qn.combExpCheckNum = -1;
             } else {
@@ -1626,9 +1672,9 @@ final class Analyser extends Parser {
                 } else {
                     varNum = qn.upper - qn.lower;
                 }
-                
+
                 if (varNum >= CEC_THRES_NUM_BIG_REPEAT) addState |= CEC_CONT_BIG_REPEAT;
-                
+
                 if (((state & CEC_IN_INFINITE_REPEAT) != 0 && varNum != 0) ||
                    ((state & CEC_CONT_BIG_REPEAT) != 0 && varNum >= CEC_THRES_NUM_BIG_REPEAT)) {
                     if (qn.combExpCheckNum == 0) {
@@ -1643,7 +1689,7 @@ final class Analyser extends Parser {
             r = setupCombExpCheck(qn.target, childState);
             r |= addState;
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             switch( en.type) {
@@ -1653,12 +1699,12 @@ final class Analyser extends Parser {
                 }
                 r = setupCombExpCheck(en.target, state);
                 break;
-            
+
             default:
                 r = setupCombExpCheck(en.target, state);
             } // inner switch
             break;
-            
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 CallNode cn = (CallNode)node;
@@ -1667,15 +1713,14 @@ final class Analyser extends Parser {
                 } else {
                     r = setupCombExpCheck(cn.target, state);
                 }
-                break;
             } // USE_SUBEXP_CALL
             break;
-            
+
         default:
             break;
-            
+
         } // switch
-        
+
         return r;
     }
 
@@ -1693,7 +1738,8 @@ final class Analyser extends Parser {
     5. find invalid patterns in look-behind.
     6. expand repeated string.
     */
-    protected final void setupTree(Node node, int state) {
+    protected final Node setupTree(Node node, int state) {
+        restart: while (true) {
         switch (node.getType()) {
         case NodeType.LIST:
             ConsAltNode lin = (ConsAltNode)node;
@@ -1706,30 +1752,30 @@ final class Analyser extends Parser {
                 prev = lin.car;
             } while ((lin = lin.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode aln = (ConsAltNode)node;
             do {
                 setupTree(aln.car, (state | IN_ALT));
             } while ((aln = aln.cdr) != null);
             break;
-            
+
         case NodeType.CCLASS:
             break;
 
         case NodeType.STR:
             if (isIgnoreCase(regex.options) && !((StringNode)node).isRaw()) {
-                expandCaseFoldString(node);
+                node = expandCaseFoldString(node);
             }
             break;
-            
+
         case NodeType.CTYPE:
         case NodeType.CANY:
             break;
-            
+
         case NodeType.CALL: // if (Config.USE_SUBEXP_CALL) ?
             break;
-            
+
         case NodeType.BREF:
             BackRefNode br = (BackRefNode)node;
             for (int i=0; i<br.backNum; i++) {
@@ -1744,13 +1790,13 @@ final class Analyser extends Parser {
                 ((EncloseNode)env.memNodes[br.back[i]]).setMemBackrefed();
             }
             break;
-            
+
         case NodeType.QTFR:
             QuantifierNode qn = (QuantifierNode)node;
             Node target = qn.target;
-            
+
             if ((state & IN_REPEAT) != 0) qn.setInRepeat();
-            
+
             if (isRepeatInfinite(qn.upper) || qn.lower >= 1) {
                 int d = getMinMatchLength(target);
                 if (d == 0) {
@@ -1762,28 +1808,28 @@ final class Analyser extends Parser {
                     // strange stuff here (turned off)
                 }
             }
-            
+
             state |= IN_REPEAT;
             if (qn.lower != qn.upper) state |= IN_VAR_REPEAT;
-            
-            setupTree(target, state);            
-            
+
+            target = setupTree(target, state);
+
             /* expand string */
             if (target.getType() == NodeType.STR) {
                 if (!isRepeatInfinite(qn.lower) && qn.lower == qn.upper &&
                     qn.lower > 1 && qn.lower <= EXPAND_STRING_MAX_LENGTH) {
                     StringNode sn = (StringNode)target;
                     int len = sn.length();
-                    
+
                     if (len * qn.lower <= EXPAND_STRING_MAX_LENGTH) {
-                        StringNode str = qn.convertToString();
-                        // if (str.parent == null) root = str; 
+                        StringNode str = qn.convertToString(sn.flag);
                         int n = qn.lower;
-                        for (int i=0; i<n; i++) {
-                           str.cat(sn.bytes, sn.p, sn.end); 
+                        for (int i = 0; i < n; i++) {
+                            str.cat(sn.bytes, sn.p, sn.end);
                         }
+                        break; /* break case NT_QTFR: */
                     }
-                    break; /* break case NT_QTFR: */
+
                 }
             }
             if (Config.USE_OP_PUSH_OR_JUMP_EXACT) {
@@ -1800,7 +1846,7 @@ final class Analyser extends Parser {
                 }
             } // USE_OP_PUSH_OR_JUMP_EXACT
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode en = (EncloseNode)node;
             switch (en.type) {
@@ -1810,12 +1856,12 @@ final class Analyser extends Parser {
                 setupTree(en.target, state);
                 regex.options = options;
                 break;
-                
+
             case EncloseType.MEMORY:
                 if ((state & (IN_ALT | IN_NOT | IN_VAR_REPEAT)) != 0) {
                     env.btMemStart = bsOnAt(env.btMemStart, en.regNum);
                     /* SET_ENCLOSE_STATUS(node, NST_MEM_IN_ALT_NOT); */
-                    
+
                 }
                 setupTree(en.target, state);
                 break;
@@ -1830,56 +1876,47 @@ final class Analyser extends Parser {
                     }
                 }
                 break;
-                
+
             } // inner switch
             break;
-            
+
         case NodeType.ANCHOR:
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
             case AnchorType.PREC_READ:
                 setupTree(an.target, state);
                 break;
-            
+
             case AnchorType.PREC_READ_NOT:
                 setupTree(an.target, (state | IN_NOT));
                 break;
-                
+
             case AnchorType.LOOK_BEHIND:
-                boolean lbInvalid = checkTypeTree(an.target, NodeType.ALLOWED_IN_LB,
-                										     EncloseType.ALLOWED_IN_LB,
-                										     AnchorType.ALLOWED_IN_LB);
-                
-                if (lbInvalid) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
-                setupLookBehind(node);
-                setupTree(an.target, state);
+                if (checkTypeTree(an.target, NodeType.ALLOWED_IN_LB, EncloseType.ALLOWED_IN_LB, AnchorType.ALLOWED_IN_LB)) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+                node = setupLookBehind(node);
+                if (node.getType() != NodeType.ANCHOR) continue restart;
+                setupTree(((AnchorNode)node).target, state);
                 break;
-                
+
             case AnchorType.LOOK_BEHIND_NOT:
-                boolean lbnInvalid = checkTypeTree(an.target, NodeType.ALLOWED_IN_LB,
-                                                              EncloseType.ALLOWED_IN_LB,
-                                                              AnchorType.ALLOWED_IN_LB);
-                
-                if (lbnInvalid) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
-                
-                setupLookBehind(node);
-                setupTree(an.target, (state | IN_NOT));
+                if (checkTypeTree(an.target, NodeType.ALLOWED_IN_LB, EncloseType.ALLOWED_IN_LB, AnchorType.ALLOWED_IN_LB)) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+                node = setupLookBehind(node);
+                if (node.getType() != NodeType.ANCHOR) continue restart;
+                setupTree(((AnchorNode)node).target, (state | IN_NOT));
                 break;
-                
-            } // inner switch
-            break;
 
-        default:
+            } // inner switch
             break;
-            
         } // switch
+        return node;
+        } // restart: while
     }
-    
+
     private static final int MAX_NODE_OPT_INFO_REF_COUNT   = 5;
     private void optimizeNodeLeft(Node node, NodeOptInfo opt, OptEnvironment oenv) { // oenv remove, pass mmd
         opt.clear();
         opt.setBoundNode(oenv.mmd);
-        
+
         switch (node.getType()) {
         case NodeType.LIST: {
             OptEnvironment nenv = new OptEnvironment();
@@ -1893,7 +1930,7 @@ final class Analyser extends Parser {
             } while ((lin = lin.cdr) != null);
             break;
         }
-        
+
         case NodeType.ALT: {
             NodeOptInfo nopt = new NodeOptInfo();
             ConsAltNode aln = (ConsAltNode)node;
@@ -1910,9 +1947,9 @@ final class Analyser extends Parser {
 
         case NodeType.STR: {
             StringNode sn = (StringNode)node;
-            
+
             int slen = sn.length();
-            
+
             if (!sn.isAmbig()) {
                 opt.exb.concatStr(sn.bytes, sn.p, sn.end, sn.isRaw(), enc);
 
@@ -1929,7 +1966,7 @@ final class Analyser extends Parser {
                 } else {
                     opt.exb.concatStr(sn.bytes, sn.p, sn.end, sn.isRaw(), enc);
                     opt.exb.ignoreCase = true;
-                    
+
                     if (slen > 0) {
                         opt.map.addCharAmb(sn.bytes, sn.p, sn.end, enc, oenv.caseFoldFlag);
                     }
@@ -1938,13 +1975,13 @@ final class Analyser extends Parser {
                 }
                 opt.length.set(slen, max);
             }
-            
+
             if (opt.exb.length == slen) {
                 opt.exb.reachEnd = true;
             }
-            break;            
+            break;
         }
-            
+
         case NodeType.CCLASS: {
             CClassNode cc = (CClassNode)node;
             /* no need to check ignore case. (setted in setup_tree()) */
@@ -1961,16 +1998,16 @@ final class Analyser extends Parser {
                 }
                 opt.length.set(1, 1);
             }
-            break;            
+            break;
         }
-            
+
         case NodeType.CTYPE: {
             int min;
             int max = enc.maxLengthDistance();
             if (max == 1) {
                 min = 1;
                 CTypeNode cn = (CTypeNode)node;
-                
+
                 switch (cn.ctype) {
                 case CharacterType.WORD:
                     if (cn.not) {
@@ -1980,7 +2017,7 @@ final class Analyser extends Parser {
                             }
                         }
                     } else {
-                        for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {                        
+                        for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
                             if (enc.isWord(i)) {
                                 opt.map.addChar((byte)i, enc);
                             }
@@ -1994,12 +2031,12 @@ final class Analyser extends Parser {
             opt.length.set(min, max);
             break;
         }
-            
+
         case NodeType.CANY: {
             opt.length.set(enc.minLength(), enc.maxLengthDistance());
             break;
         }
-        
+
         case NodeType.ANCHOR: {
             AnchorNode an = (AnchorNode)node;
             switch (an.type) {
@@ -2011,7 +2048,7 @@ final class Analyser extends Parser {
             case AnchorType.END_LINE:
                 opt.anchor.add(an.type);
                 break;
-                
+
             case AnchorType.PREC_READ:
                 NodeOptInfo nopt = new NodeOptInfo();
                 optimizeNodeLeft(an.target, nopt, oenv);
@@ -2028,24 +2065,24 @@ final class Analyser extends Parser {
             case AnchorType.LOOK_BEHIND:    /* Sorry, I can't make use of it. */
             case AnchorType.LOOK_BEHIND_NOT:
                 break;
-                
+
             } // inner switch
             break;
         }
-            
+
         case NodeType.BREF: {
             BackRefNode br = (BackRefNode)node;
-            
+
             if (br.isRecursion()) {
                 opt.length.set(0, MinMaxLen.INFINITE_DISTANCE);
                 break;
             }
-            
+
             Node[]nodes = oenv.scanEnv.memNodes;
-            
+
             int min = getMinMatchLength(nodes[br.back[0]]);
             int max = getMaxMatchLength(nodes[br.back[0]]);
-            
+
             for (int i=1; i<br.backNum; i++) {
                 int tmin = getMinMatchLength(nodes[br.back[i]]);
                 int tmax = getMaxMatchLength(nodes[br.back[i]]);
@@ -2067,7 +2104,6 @@ final class Analyser extends Parser {
                     optimizeNodeLeft(cn.target, opt, oenv);
                     oenv.options = safe;
                 }
-                break;
             } // USE_SUBEXP_CALL
             break;
         }
@@ -2090,7 +2126,7 @@ final class Analyser extends Parser {
                     if (nopt.exb.length > 0) {
                         if (nopt.exb.reachEnd) {
                             int i;
-                            for (i=1; i<qn.lower && !opt.exb.isFull(); i++) {
+                            for (i = 2; i <= qn.lower && !opt.exb.isFull(); i++) {
                                 opt.exb.concat(nopt.exb, enc);
                             }
                             if (i < qn.lower) {
@@ -2105,7 +2141,7 @@ final class Analyser extends Parser {
                     if (qn.lower > 1) {
                         opt.exm.reachEnd = false;
                     }
-                    
+
                 }
             }
             int min = MinMaxLen.distanceMultiply(nopt.length.min, qn.lower);
@@ -2128,7 +2164,7 @@ final class Analyser extends Parser {
                 optimizeNodeLeft(en.target, opt, oenv);
                 oenv.options = save;
                 break;
-                
+
             case EncloseType.MEMORY:
                 if (Config.USE_SUBEXP_CALL && ++en.optCount > MAX_NODE_OPT_INFO_REF_COUNT) {
                     int min = 0;
@@ -2139,35 +2175,35 @@ final class Analyser extends Parser {
                 } else { // USE_SUBEXP_CALL
                     optimizeNodeLeft(en.target, opt, oenv);
                     if (opt.anchor.isSet(AnchorType.ANYCHAR_STAR_MASK)) {
-                        if (bsAt(oenv.scanEnv.backrefedMem, en.regNum)) { 
+                        if (bsAt(oenv.scanEnv.backrefedMem, en.regNum)) {
                             opt.anchor.remove(AnchorType.ANYCHAR_STAR_MASK);
                         }
                     }
-                } 
+                }
                 break;
-                
+
             case EncloseType.STOP_BACKTRACK:
                 optimizeNodeLeft(en.target, opt, oenv);
                 break;
             } // inner switch
             break;
         }
-        
+
         default:
             newInternalException(ERR_PARSER_BUG);
         } // switch
     }
-    
-    protected final void setOptimizedInfoFromTree(Node node) {    
+
+    protected final void setOptimizedInfoFromTree(Node node) {
         NodeOptInfo opt = new NodeOptInfo();
         OptEnvironment oenv = new OptEnvironment();
-        
+
         oenv.enc = regex.enc;
         oenv.options = regex.options;
         oenv.caseFoldFlag = regex.caseFoldFlag;
         oenv.scanEnv = env;
         oenv.mmd.clear(); // ??
-        
+
         optimizeNodeLeft(node, opt, oenv);
 
         regex.anchor = opt.anchor.leftAnchor & (AnchorType.BEGIN_BUF |
@@ -2201,7 +2237,7 @@ final class Analyser extends Parser {
             regex.subAnchor |= opt.anchor.leftAnchor & AnchorType.BEGIN_LINE;
             if (opt.length.max == 0) regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
         }
-        
+
         if (Config.DEBUG_COMPILE || Config.DEBUG_MATCH) {
             Config.log.println(regex.optimizeInfoToString());
         }
diff --git a/src/org/joni/ApplyCaseFold.java b/src/org/joni/ApplyCaseFold.java
index 54a1aa0..7dd84ce 100644
--- a/src/org/joni/ApplyCaseFold.java
+++ b/src/org/joni/ApplyCaseFold.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -35,7 +35,7 @@ final class ApplyCaseFold implements ApplyAllCaseFoldFunction {
         Encoding enc = env.enc;
         CClassNode cc = arg.cc;
         BitSet bs = cc.bs;
-        
+
         if (length == 1) {
             boolean inCC = cc.isCodeInCC(enc, from);
 
@@ -62,23 +62,18 @@ final class ApplyCaseFold implements ApplyAllCaseFoldFunction {
                     }
                 }
             } // CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS
-            
+
         } else {
             if (cc.isCodeInCC(enc, from) && (!Config.CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS || !cc.isNot())) {
                 StringNode node = null;
                 for (int i=0; i<length; i++) {
                     if (i == 0) {
                         node = new StringNode();
-                        node.ensure(Config.ENC_CODE_TO_MBC_MAXLEN);
-                        node.end += enc.codeToMbc(to[i], node.bytes, node.end);
-                        
                         /* char-class expanded multi-char only
                         compare with string folded at match time. */
                         node.setAmbig();
-                    } else {
-                        node.ensure(Config.ENC_CODE_TO_MBC_MAXLEN);
-                        node.end += enc.codeToMbc(to[i], node.bytes, node.end);
                     }
+                    node.catCode(to[i], enc);
                 }
 
                 ConsAltNode alt = ConsAltNode.newAltNode(node, null);
@@ -94,6 +89,6 @@ final class ApplyCaseFold implements ApplyAllCaseFoldFunction {
         }
 
     }
-    
-    static final ApplyCaseFold INSTANCE = new ApplyCaseFold();   
+
+    static final ApplyCaseFold INSTANCE = new ApplyCaseFold();
 }
diff --git a/src/org/joni/ApplyCaseFoldArg.java b/src/org/joni/ApplyCaseFoldArg.java
index 92dd2fb..10b297f 100644
--- a/src/org/joni/ApplyCaseFoldArg.java
+++ b/src/org/joni/ApplyCaseFoldArg.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -27,7 +27,7 @@ public final class ApplyCaseFoldArg {
     final CClassNode cc;
     ConsAltNode altRoot;
     ConsAltNode tail;
-    
+
     public ApplyCaseFoldArg(ScanEnvironment env, CClassNode cc) {
         this.env = env;
         this.cc = cc;
diff --git a/src/org/joni/ArrayCompiler.java b/src/org/joni/ArrayCompiler.java
index f23ce58..ac21d0e 100644
--- a/src/org/joni/ArrayCompiler.java
+++ b/src/org/joni/ArrayCompiler.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -44,6 +44,11 @@ import org.joni.constants.OPSize;
 import org.joni.constants.TargetInfo;
 
 final class ArrayCompiler extends Compiler {
+    private int[]code;
+    private int codeLength;
+
+    private byte[][]templates;
+    private int templateNum;
 
     ArrayCompiler(Analyser analyser) {
         super(analyser);
@@ -51,21 +56,26 @@ final class ArrayCompiler extends Compiler {
 
     @Override
     protected final void prepare() {
-        regex.code = new int[(analyser.stop - analyser.p) * 2 + 1]; // (+1: empty regex)        
-        regex.codeLength = 0;
+        int codeSize = Config.USE_STRING_TEMPLATES ? 8 : ((analyser.getEnd() - analyser.getBegin()) * 2 + 2);
+        code = new int[codeSize];
+        codeLength = 0;
     }
 
     @Override
     protected final void finish() {
         addOpcode(OPCode.END);
         addOpcode(OPCode.FINISH); // for stack bottom
-        
+
+        regex.code = code;
+        regex.codeLength = codeLength;
+        regex.templates = templates;
+        regex.templateNum = templateNum;
+        regex.factory = MatcherFactory.DEFAULT;
+
         if (Config.USE_SUBEXP_CALL && analyser.env.unsetAddrList != null) {
             analyser.env.unsetAddrList.fix(regex);
             analyser.env.unsetAddrList = null;
         }
-
-        regex.factory = MatcherFactory.DEFAULT;
     }
 
     @Override
@@ -80,7 +90,7 @@ final class ArrayCompiler extends Compiler {
             }
         } while ((aln = aln.cdr) != null);
 
-        int pos = regex.codeLength + len;  /* goal position */
+        int pos = codeLength + len;  /* goal position */
 
         aln = node;
         do {
@@ -90,7 +100,7 @@ final class ArrayCompiler extends Compiler {
             }
             compileTree(aln.car);
             if (aln.cdr != null) {
-                len = pos - (regex.codeLength + OPSize.JUMP);
+                len = pos - (codeLength + OPSize.JUMP);
                 addOpcodeRelAddr(OPCode.JUMP, len);
             }
         } while ((aln = aln.cdr) != null);
@@ -105,9 +115,13 @@ final class ArrayCompiler extends Compiler {
                 op == OPCode.EXACTN_IC_SB;
     }
 
+    private boolean opTemplated(int op) {
+        return isNeedStrLenOpExact(op);
+    }
+
     private int selectStrOpcode(int mbLength, int strLength, boolean ignoreCase) {
         int op;
-        
+
         if (ignoreCase) {
             switch(strLength) {
             case 1: op = enc.toLowerCaseTable() != null ? OPCode.EXACT1_IC_SB : OPCode.EXACT1_IC; break;
@@ -135,6 +149,7 @@ final class ArrayCompiler extends Compiler {
                 break;
             case 3:
                 op = OPCode.EXACTMB3N;
+                break;
             default:
                 op = OPCode.EXACTMBN;
             } // switch
@@ -144,15 +159,15 @@ final class ArrayCompiler extends Compiler {
 
     private void compileTreeEmptyCheck(Node node, int emptyInfo) {
         int savedNumNullCheck = regex.numNullCheck;
-        
+
         if (emptyInfo != 0) {
             addOpcode(OPCode.NULL_CHECK_START);
             addMemNum(regex.numNullCheck); /* NULL CHECK ID */
             regex.numNullCheck++;
         }
-        
+
         compileTree(node);
-        
+
         if (emptyInfo != 0) {
             switch(emptyInfo) {
             case TargetInfo.IS_EMPTY:
@@ -165,20 +180,23 @@ final class ArrayCompiler extends Compiler {
                 addOpcode(OPCode.NULL_CHECK_END_MEMST_PUSH);
                 break;
             } // switch
-            
+
             addMemNum(savedNumNullCheck); /* NULL CHECK ID */
         }
     }
 
     private int addCompileStringlength(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
         int op = selectStrOpcode(mbLength, strLength, ignoreCase);
-        
         int len = OPSize.OPCODE;
 
+        if (Config.USE_STRING_TEMPLATES && opTemplated(op)) {
+            // string length, template index, template string pointer
+            len += OPSize.LENGTH + OPSize.INDEX + OPSize.INDEX;
+        } else {
+            if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
+            len += mbLength * strLength;
+        }
         if (op == OPCode.EXACTMBN) len += OPSize.LENGTH;
-        if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
-        
-        len += mbLength * strLength;
         return len;
     }
 
@@ -186,34 +204,41 @@ final class ArrayCompiler extends Compiler {
     protected final void addCompileString(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
         int op = selectStrOpcode(mbLength, strLength, ignoreCase);
         addOpcode(op);
-        
+
         if (op == OPCode.EXACTMBN) addLength(mbLength);
-        
+
         if (isNeedStrLenOpExact(op)) {
             if (op == OPCode.EXACTN_IC || op == OPCode.EXACTN_IC_SB) {
                 addLength(mbLength * strLength);
             } else {
-                addLength(strLength);                
+                addLength(strLength);
             }
         }
-        addBytes(bytes, p, mbLength * strLength);
+
+        if (Config.USE_STRING_TEMPLATES && opTemplated(op)) {
+            addInt(templateNum);
+            addInt(p);
+            addTemplate(bytes);
+        } else {
+            addBytes(bytes, p, mbLength * strLength);
+        }
     }
 
     private int compileLengthStringNode(Node node) {
         StringNode sn = (StringNode)node;
         if (sn.length() <= 0) return 0;
         boolean ambig = sn.isAmbig();
-        
+
         int p, prev;
         p = prev = sn.p;
         int end = sn.end;
         byte[]bytes = sn.bytes;
         int prevLen = enc.length(bytes, p, end);
         p += prevLen;
-        
+
         int slen = 1;
         int rlen = 0;
-        
+
         while (p < end) {
             int len = enc.length(bytes, p, end);
             if (len == prevLen) {
@@ -231,9 +256,9 @@ final class ArrayCompiler extends Compiler {
         rlen += r;
         return rlen;
     }
-    
+
     private int compileLengthStringRawNode(StringNode sn) {
-        if (sn.length() <= 0) return 0;        
+        if (sn.length() <= 0) return 0;
         return addCompileStringlength(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
     }
 
@@ -241,7 +266,7 @@ final class ArrayCompiler extends Compiler {
         addLength(mbuf.used);
         addInts(mbuf.p, mbuf.used);
     }
-    
+
     private int compileLengthCClassNode(CClassNode cc) {
         if (cc.isShare()) return OPSize.OPCODE + OPSize.POINTER;
 
@@ -254,7 +279,7 @@ final class ArrayCompiler extends Compiler {
             } else {
                 len = OPSize.OPCODE + BitSet.BITSET_SIZE;
             }
-            
+
             len += OPSize.LENGTH + cc.mbuf.used;
         }
         return len;
@@ -267,7 +292,7 @@ final class ArrayCompiler extends Compiler {
             addPointer(cc);
             return;
         }
-        
+
         if (cc.mbuf == null) {
             if (cc.isNot()) {
                 addOpcode(enc.isSingleByte() ? OPCode.CCLASS_NOT_SB : OPCode.CCLASS_NOT);
@@ -282,7 +307,7 @@ final class ArrayCompiler extends Compiler {
                 } else {
                     addOpcode(OPCode.CCLASS_MB);
                 }
-                addMultiByteCClass(cc.mbuf);                
+                addMultiByteCClass(cc.mbuf);
             } else {
                 if (cc.isNot()) {
                     addOpcode(OPCode.CCLASS_MIX_NOT);
@@ -308,17 +333,17 @@ final class ArrayCompiler extends Compiler {
                 op = enc.isSingleByte() ? OPCode.WORD_SB : OPCode.WORD;
             }
             break;
-            
+
         default:
             newInternalException(ERR_PARSER_BUG);
-            return; // not reached  
+            return; // not reached
         } // inner switch
         addOpcode(op);
     }
 
     @Override
     protected void compileAnyCharNode() {
-        if (isMultiline(regex.options)) {            
+        if (isMultiline(regex.options)) {
             addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_ML_SB : OPCode.ANYCHAR_ML);
         } else {
             addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_SB : OPCode.ANYCHAR);
@@ -328,7 +353,7 @@ final class ArrayCompiler extends Compiler {
     @Override
     protected void compileCallNode(CallNode node) {
         addOpcode(OPCode.CALL);
-        node.unsetAddrList.add(regex.codeLength, node.target);
+        node.unsetAddrList.add(codeLength, node.target);
         addAbsAddr(0); /*dummy addr.*/
     }
 
@@ -341,7 +366,7 @@ final class ArrayCompiler extends Compiler {
             addLength(br.nestLevel);
             // !goto add_bacref_mems;!
             addLength(br.backNum);
-            for (int i=br.backNum-1; i>=0; i--) addMemNum(br.back[i]);                        
+            for (int i=br.backNum-1; i>=0; i--) addMemNum(br.back[i]);
             return;
         } else { // USE_BACKREF_AT_LEVEL
             if (br.backNum == 1) {
@@ -370,7 +395,7 @@ final class ArrayCompiler extends Compiler {
                 }
                 // !add_bacref_mems:!
                 addLength(br.backNum);
-                for (int i=br.backNum-1; i>=0; i--) addMemNum(br.back[i]);                        
+                for (int i=br.backNum-1; i>=0; i--) addMemNum(br.back[i]);
             }
         }
     }
@@ -388,45 +413,45 @@ final class ArrayCompiler extends Compiler {
             System.arraycopy(regex.repeatRangeHi, 0, tmp, 0, regex.repeatRangeHi.length);
             regex.repeatRangeHi = tmp;
         }
-        
+
         regex.repeatRangeLo[id] = lower;
         regex.repeatRangeHi[id] = isRepeatInfinite(upper) ? 0x7fffffff : upper;
     }
-    
+
     private void compileRangeRepeatNode(QuantifierNode qn, int targetLen, int emptyInfo) {
         int numRepeat = regex.numRepeat;
         addOpcode(qn.greedy ? OPCode.REPEAT : OPCode.REPEAT_NG);
         addMemNum(numRepeat); /* OP_REPEAT ID */
         regex.numRepeat++;
         addRelAddr(targetLen + OPSize.REPEAT_INC);
-        
+
         entryRepeatRange(numRepeat, qn.lower, qn.upper);
-        
+
         compileTreeEmptyCheck(qn.target, emptyInfo);
-        
+
         if ((Config.USE_SUBEXP_CALL && regex.numCall > 0) || qn.isInRepeat()) {
             addOpcode(qn.greedy ? OPCode.REPEAT_INC_SG : OPCode.REPEAT_INC_NG_SG);
         } else {
-            addOpcode(qn.greedy ? OPCode.REPEAT_INC : OPCode.REPEAT_INC_NG);            
+            addOpcode(qn.greedy ? OPCode.REPEAT_INC : OPCode.REPEAT_INC_NG);
         }
-        
+
         addMemNum(numRepeat); /* OP_REPEAT ID */
     }
-    
+
     private static final int QUANTIFIER_EXPAND_LIMIT_SIZE   = 50; // was 50
 
-    private static boolean cknOn(int ckn) { 
+    private static boolean cknOn(int ckn) {
         return ckn > 0;
     }
-    
+
     private int compileCECLengthQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
-        
+
         int tlen = compileLengthTree(qn.target);
         int ckn = regex.numCombExpCheck > 0 ? qn.combExpCheckNum : 0;
         int cklen = cknOn(ckn) ? OPSize.STATE_CHECK_NUM : 0;
-        
+
         /* anychar repeat */
         if (qn.target.getType() == NodeType.CANY) {
             if (qn.greedy && infinite) {
@@ -437,14 +462,14 @@ final class ArrayCompiler extends Compiler {
                 }
             }
         }
-        
+
         int modTLen;
         if (emptyInfo != 0) {
             modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
         } else {
             modTLen = tlen;
         }
-        
+
         int len;
         if (infinite && qn.lower <= 1) {
             if (qn.greedy) {
@@ -482,7 +507,7 @@ final class ArrayCompiler extends Compiler {
             len = OPSize.PUSH + cklen + OPSize.JUMP + tlen;
         } else {
             len = OPSize.REPEAT_INC + modTLen + OPSize.OPCODE + OPSize.RELADDR + OPSize.MEMNUM;
-            
+
             if (cknOn(ckn)) {
                 len += OPSize.STATE_CHECK;
             }
@@ -494,11 +519,11 @@ final class ArrayCompiler extends Compiler {
     protected void compileCECQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
-        
+
         int tlen = compileLengthTree(qn.target);
-        
+
         int ckn = regex.numCombExpCheck > 0 ? qn.combExpCheckNum : 0;
-        
+
         if (qn.isAnyCharStar()) {
             compileTreeNTimes(qn.target, qn.lower);
             if (qn.nextHeadExact != null && !cknOn(ckn)) {
@@ -533,7 +558,7 @@ final class ArrayCompiler extends Compiler {
                 return;
             }
         }
-        
+
         int modTLen;
         if (emptyInfo != 0) {
             modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
@@ -594,7 +619,7 @@ final class ArrayCompiler extends Compiler {
             } else {
                 addOpcodeRelAddr(OPCode.PUSH, OPSize.JUMP);
             }
-            
+
             addOpcodeRelAddr(OPCode.JUMP, tlen);
             compileTree(qn.target);
         } else {
@@ -609,7 +634,7 @@ final class ArrayCompiler extends Compiler {
     private int compileNonCECLengthQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
-        
+
         int tlen = compileLengthTree(qn.target);
 
         /* anychar repeat */
@@ -622,14 +647,14 @@ final class ArrayCompiler extends Compiler {
                 }
             }
         }
-        
+
         int modTLen = 0;
         if (emptyInfo != 0) {
             modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
         } else {
             modTLen = tlen;
         }
-        
+
         int len;
         if (infinite && (qn.lower <= 1 || tlen * qn.lower <= QUANTIFIER_EXPAND_LIMIT_SIZE)) {
             if (qn.lower == 1 && tlen > QUANTIFIER_EXPAND_LIMIT_SIZE) {
@@ -637,7 +662,7 @@ final class ArrayCompiler extends Compiler {
             } else {
                 len = tlen * qn.lower;
             }
-            
+
             if (qn.greedy) {
                 if (qn.headExact != null) {
                     len += OPSize.PUSH_OR_JUMP_EXACT1 + modTLen + OPSize.JUMP;
@@ -647,13 +672,13 @@ final class ArrayCompiler extends Compiler {
                     len += OPSize.PUSH + modTLen + OPSize.JUMP;
                 }
             } else {
-                len += OPSize.JUMP + modTLen + OPSize.PUSH; 
+                len += OPSize.JUMP + modTLen + OPSize.PUSH;
             }
-            
+
         } else if (qn.upper == 0 && qn.isRefered) { /* /(?<n>..){0}/ */
             len = OPSize.JUMP + tlen;
         } else if (!infinite && qn.greedy &&
-                  (qn.upper == 1 || (tlen + OPSize.PUSH) * qn.upper <= QUANTIFIER_EXPAND_LIMIT_SIZE )) { 
+                  (qn.upper == 1 || (tlen + OPSize.PUSH) * qn.upper <= QUANTIFIER_EXPAND_LIMIT_SIZE )) {
             len = tlen * qn.lower;
             len += (OPSize.PUSH + tlen) * (qn.upper - qn.lower);
         } else if (!qn.greedy && qn.upper == 1 && qn.lower == 0) { /* '??' */
@@ -663,14 +688,14 @@ final class ArrayCompiler extends Compiler {
         }
         return len;
     }
-    
+
     @Override
     protected void compileNonCECQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
-        
-        int tlen = compileLengthTree(qn.target);        
-        
+
+        int tlen = compileLengthTree(qn.target);
+
         if (qn.isAnyCharStar()) {
             compileTreeNTimes(qn.target, qn.lower);
             if (qn.nextHeadExact != null) {
@@ -691,7 +716,7 @@ final class ArrayCompiler extends Compiler {
                 return;
             }
         }
-        
+
         int modTLen;
         if (emptyInfo != 0) {
             modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
@@ -714,7 +739,7 @@ final class ArrayCompiler extends Compiler {
             } else {
                 compileTreeNTimes(qn.target, qn.lower);
             }
-                
+
             if (qn.greedy) {
                 if (qn.headExact != null) {
                     addOpcodeRelAddr(OPCode.PUSH_OR_JUMP_EXACT1, modTLen + OPSize.JUMP);
@@ -722,7 +747,7 @@ final class ArrayCompiler extends Compiler {
                     addBytes(sn.bytes, sn.p, 1);
                     compileTreeEmptyCheck(qn.target, emptyInfo);
                     addOpcodeRelAddr(OPCode.JUMP, -(modTLen + OPSize.JUMP + OPSize.PUSH_OR_JUMP_EXACT1));
-                } else if (qn.nextHeadExact != null) { 
+                } else if (qn.nextHeadExact != null) {
                     addOpcodeRelAddr(OPCode.PUSH_IF_PEEK_NEXT, modTLen + OPSize.JUMP);
                     StringNode sn = (StringNode)qn.nextHeadExact;
                     addBytes(sn.bytes, sn.p, 1);
@@ -741,11 +766,11 @@ final class ArrayCompiler extends Compiler {
         } else if (qn.upper == 0 && qn.isRefered) { /* /(?<n>..){0}/ */
             addOpcodeRelAddr(OPCode.JUMP, tlen);
             compileTree(qn.target);
-        } else if (!infinite && qn.greedy && 
+        } else if (!infinite && qn.greedy &&
                   (qn.upper == 1 || (tlen + OPSize.PUSH) * qn.upper <= QUANTIFIER_EXPAND_LIMIT_SIZE)) {
             int n = qn.upper - qn.lower;
             compileTreeNTimes(qn.target, qn.lower);
-            
+
             for (int i=0; i<n; i++) {
                 addOpcodeRelAddr(OPCode.PUSH, (n - i) * tlen + (n - i - 1) * OPSize.PUSH);
                 compileTree(qn.target);
@@ -764,7 +789,7 @@ final class ArrayCompiler extends Compiler {
         regex.options = node.option;
         int tlen = compileLengthTree(node.target);
         regex.options = prev;
-        
+
         if (isDynamic(prev ^ node.option)) {
             return OPSize.SET_OPTION_PUSH + OPSize.SET_OPTION + OPSize.FAIL + tlen + OPSize.SET_OPTION;
         } else {
@@ -775,17 +800,17 @@ final class ArrayCompiler extends Compiler {
     @Override
     protected void compileOptionNode(EncloseNode node) {
         int prev = regex.options;
-        
+
         if (isDynamic(prev ^ node.option)) {
             addOpcodeOption(OPCode.SET_OPTION_PUSH, node.option);
             addOpcodeOption(OPCode.SET_OPTION, prev);
             addOpcode(OPCode.FAIL);
         }
-        
+
         regex.options = node.option;
         compileTree(node.target);
         regex.options = prev;
-        
+
         if (isDynamic(prev ^ node.option)) {
             addOpcodeOption(OPCode.SET_OPTION, prev);
         }
@@ -795,14 +820,14 @@ final class ArrayCompiler extends Compiler {
         if (node.isOption()) {
             return compileLengthOptionNode(node);
         }
-        
+
         int tlen;
         if (node.target != null) {
             tlen = compileLengthTree(node.target);
         } else {
             tlen = 0;
         }
-        
+
         int len;
         switch (node.type) {
         case EncloseType.MEMORY:
@@ -822,17 +847,17 @@ final class ArrayCompiler extends Compiler {
                 len += tlen + (bsAt(regex.btMemEnd, node.regNum) ? OPSize.MEMORY_END_PUSH : OPSize.MEMORY_END);
             }
             break;
-            
+
         case EncloseType.STOP_BACKTRACK:
             if (node.isStopBtSimpleRepeat()) {
                 QuantifierNode qn = (QuantifierNode)node.target;
                 tlen = compileLengthTree(qn.target);
                 len = tlen * qn.lower + OPSize.PUSH + tlen + OPSize.POP + OPSize.JUMP;
-            } else { 
+            } else {
                 len = OPSize.PUSH_STOP_BT + tlen + OPSize.POP_STOP_BT;
             }
             break;
-            
+
         default:
             newInternalException(ERR_PARSER_BUG);
             return 0; // not reached
@@ -848,7 +873,7 @@ final class ArrayCompiler extends Compiler {
             if (Config.USE_SUBEXP_CALL) {
                 if (node.isCalled()) {
                     addOpcode(OPCode.CALL);
-                    node.callAddr = regex.codeLength + OPSize.ABSADDR + OPSize.JUMP;
+                    node.callAddr = codeLength + OPSize.ABSADDR + OPSize.JUMP;
                     node.setAddrFixed();
                     addAbsAddr(node.callAddr);
                     len = compileLengthTree(node.target);
@@ -861,16 +886,16 @@ final class ArrayCompiler extends Compiler {
                     addOpcodeRelAddr(OPCode.JUMP, len);
                 }
             } // USE_SUBEXP_CALL
-            
+
             if (bsAt(regex.btMemStart, node.regNum)) {
                 addOpcode(OPCode.MEMORY_START_PUSH);
             } else {
                 addOpcode(OPCode.MEMORY_START);
             }
-            
+
             addMemNum(node.regNum);
             compileTree(node.target);
-            
+
             if (Config.USE_SUBEXP_CALL && node.isCalled()) {
                 if (bsAt(regex.btMemEnd, node.regNum)) {
                     addOpcode(node.isRecursion() ? OPCode.MEMORY_END_PUSH_REC : OPCode.MEMORY_END_PUSH);
@@ -888,14 +913,14 @@ final class ArrayCompiler extends Compiler {
                 addMemNum(node.regNum);
             }
             break;
-            
+
         case EncloseType.STOP_BACKTRACK:
             if (node.isStopBtSimpleRepeat()) {
                 QuantifierNode qn = (QuantifierNode)node.target;
-                
+
                 compileTreeNTimes(qn.target, qn.lower);
-                
-                len = compileLengthTree(qn.target);                
+
+                len = compileLengthTree(qn.target);
                 addOpcodeRelAddr(OPCode.PUSH, len + OPSize.POP + OPSize.JUMP);
                 compileTree(qn.target);
                 addOpcode(OPCode.POP);
@@ -906,13 +931,13 @@ final class ArrayCompiler extends Compiler {
                 addOpcode(OPCode.POP_STOP_BT);
             }
             break;
-            
+
         default:
             newInternalException(ERR_PARSER_BUG);
             break;
         } // switch
     }
-    
+
     private int compileLengthAnchorNode(AnchorNode node) {
         int tlen;
         if (node.target != null) {
@@ -920,25 +945,25 @@ final class ArrayCompiler extends Compiler {
         } else {
             tlen = 0;
         }
-        
+
         int len;
         switch (node.type) {
         case AnchorType.PREC_READ:
             len = OPSize.PUSH_POS + tlen + OPSize.POP_POS;
             break;
-        
+
         case AnchorType.PREC_READ_NOT:
             len = OPSize.PUSH_POS_NOT + tlen + OPSize.FAIL_POS;
             break;
-        
+
         case AnchorType.LOOK_BEHIND:
             len = OPSize.LOOK_BEHIND + tlen;
             break;
-        
+
         case AnchorType.LOOK_BEHIND_NOT:
             len = OPSize.PUSH_LOOK_BEHIND_NOT + tlen + OPSize.FAIL_LOOK_BEHIND_NOT;
             break;
-            
+
         default:
             len = OPSize.OPCODE;
             break;
@@ -950,33 +975,33 @@ final class ArrayCompiler extends Compiler {
     protected void compileAnchorNode(AnchorNode node) {
         int len;
         int n;
-        
+
         switch (node.type) {
         case AnchorType.BEGIN_BUF:          addOpcode(OPCode.BEGIN_BUF);            break;
         case AnchorType.END_BUF:            addOpcode(OPCode.END_BUF);              break;
-        case AnchorType.BEGIN_LINE:         addOpcode(OPCode.BEGIN_LINE);           break;        
+        case AnchorType.BEGIN_LINE:         addOpcode(OPCode.BEGIN_LINE);           break;
         case AnchorType.END_LINE:           addOpcode(OPCode.END_LINE);             break;
         case AnchorType.SEMI_END_BUF:       addOpcode(OPCode.SEMI_END_BUF);         break;
-        case AnchorType.BEGIN_POSITION:     addOpcode(OPCode.BEGIN_POSITION);       break;        
+        case AnchorType.BEGIN_POSITION:     addOpcode(OPCode.BEGIN_POSITION);       break;
 
-        case AnchorType.WORD_BOUND:         
+        case AnchorType.WORD_BOUND:
             addOpcode(enc.isSingleByte() ? OPCode.WORD_BOUND_SB : OPCode.WORD_BOUND);
             break;
-            
+
         case AnchorType.NOT_WORD_BOUND:
             addOpcode(enc.isSingleByte() ? OPCode.NOT_WORD_BOUND_SB : OPCode.NOT_WORD_BOUND);
             break;
-        
+
         case AnchorType.WORD_BEGIN:
             if (Config.USE_WORD_BEGIN_END)
                 addOpcode(enc.isSingleByte() ? OPCode.WORD_BEGIN_SB : OPCode.WORD_BEGIN);
             break;
-        
+
         case AnchorType.WORD_END:
             if (Config.USE_WORD_BEGIN_END)
                 addOpcode(enc.isSingleByte() ? OPCode.WORD_END_SB : OPCode.WORD_END);
-            break;        
-            
+            break;
+
         case AnchorType.PREC_READ:
             addOpcode(OPCode.PUSH_POS);
             compileTree(node.target);
@@ -989,7 +1014,7 @@ final class ArrayCompiler extends Compiler {
             compileTree(node.target);
             addOpcode(OPCode.FAIL_POS);
             break;
-            
+
         case AnchorType.LOOK_BEHIND:
             addOpcode(enc.isSingleByte() ? OPCode.LOOK_BEHIND_SB : OPCode.LOOK_BEHIND);
             if (node.charLength < 0) {
@@ -1001,7 +1026,7 @@ final class ArrayCompiler extends Compiler {
             addLength(n);
             compileTree(node.target);
             break;
-            
+
         case AnchorType.LOOK_BEHIND_NOT:
             len = compileLengthTree(node.target);
             addOpcodeRelAddr(OPCode.PUSH_LOOK_BEHIND_NOT, len + OPSize.FAIL_LOOK_BEHIND_NOT);
@@ -1015,7 +1040,7 @@ final class ArrayCompiler extends Compiler {
             compileTree(node.target);
             addOpcode(OPCode.FAIL_LOOK_BEHIND_NOT);
             break;
-            
+
         default:
             newInternalException(ERR_PARSER_BUG);
         } // switch
@@ -1023,7 +1048,7 @@ final class ArrayCompiler extends Compiler {
 
     private int compileLengthTree(Node node) {
         int len = 0;
-        
+
         switch (node.getType()) {
         case NodeType.LIST:
             ConsAltNode lin = (ConsAltNode)node;
@@ -1031,7 +1056,7 @@ final class ArrayCompiler extends Compiler {
                 len += compileLengthTree(lin.car);
             } while ((lin = lin.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             ConsAltNode aln = (ConsAltNode)node;
             int n = 0;
@@ -1041,7 +1066,7 @@ final class ArrayCompiler extends Compiler {
             } while ((aln = aln.cdr) != null);
             len += (OPSize.PUSH + OPSize.JUMP) * (n - 1);
             break;
-            
+
         case NodeType.STR:
             StringNode sn = (StringNode)node;
             if (sn.isRaw()) {
@@ -1050,21 +1075,21 @@ final class ArrayCompiler extends Compiler {
                 len = compileLengthStringNode(sn);
             }
             break;
-            
+
         case NodeType.CCLASS:
             len = compileLengthCClassNode((CClassNode)node);
             break;
-            
+
         case NodeType.CTYPE:
         case NodeType.CANY:
             len = OPSize.OPCODE;
             break;
-            
+
         case NodeType.BREF:
             BackRefNode br = (BackRefNode)node;
-            
+
             if (Config.USE_BACKREF_WITH_LEVEL && br.isNestLevel()) {
-                len = OPSize.OPCODE + OPSize.OPTION + OPSize.LENGTH + 
+                len = OPSize.OPCODE + OPSize.OPTION + OPSize.LENGTH +
                       OPSize.LENGTH + (OPSize.MEMNUM * br.backNum);
             } else { // USE_BACKREF_AT_LEVEL
                 if (br.backNum == 1) {
@@ -1075,14 +1100,14 @@ final class ArrayCompiler extends Compiler {
                 }
             }
             break;
-            
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 len = OPSize.CALL;
                 break;
             } // USE_SUBEXP_CALL
             break;
-            
+
         case NodeType.QTFR:
             if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
                 len = compileCECLengthQuantifierNode((QuantifierNode)node);
@@ -1090,74 +1115,74 @@ final class ArrayCompiler extends Compiler {
                 len = compileNonCECLengthQuantifierNode((QuantifierNode)node);
             }
             break;
-            
+
         case NodeType.ENCLOSE:
             len = compileLengthEncloseNode((EncloseNode)node);
             break;
-            
+
         case NodeType.ANCHOR:
             len = compileLengthAnchorNode((AnchorNode)node);
             break;
 
         default:
             newInternalException(ERR_PARSER_BUG);
-            
+
         } //switch
         return len;
     }
 
     private void ensure(int size) {
-        if (size >= regex.code.length) {
-            int length = regex.code.length << 1;
+        if (size >= code.length) {
+            int length = code.length << 1;
             while (length <= size) length <<= 1;
             int[]tmp = new int[length];
-            System.arraycopy(regex.code, 0, tmp, 0, regex.code.length);
-            regex.code = tmp;
+            System.arraycopy(code, 0, tmp, 0, code.length);
+            code = tmp;
         }
     }
-    
+
     private void addInt(int i) {
-        if (regex.codeLength >= regex.code.length) {
-            int[]tmp = new int[regex.code.length << 1];
-            System.arraycopy(regex.code, 0, tmp, 0, regex.code.length);
-            regex.code = tmp;
+        if (codeLength >= code.length) {
+            int[]tmp = new int[code.length << 1];
+            System.arraycopy(code, 0, tmp, 0, code.length);
+            code = tmp;
         }
-        regex.code[regex.codeLength++] = i;
+        code[codeLength++] = i;
     }
-    
+
     void setInt(int i, int offset) {
         ensure(offset);
         regex.code[offset] = i;
     }
-    
+
     private void addObject(Object o) {
         if (regex.operands == null) {
             regex.operands = new Object[4];
         } else if (regex.operandLength >= regex.operands.length) {
-            Object[]tmp = new Object[regex.operands.length << 1];            
-            System.arraycopy(regex.operands, 0, tmp, 0, regex.operands.length);            
-            regex.operands = tmp;            
+            Object[]tmp = new Object[regex.operands.length << 1];
+            System.arraycopy(regex.operands, 0, tmp, 0, regex.operands.length);
+            regex.operands = tmp;
         }
         addInt(regex.operandLength);
         regex.operands[regex.operandLength++] = o;
     }
-    
+
     private void addBytes(byte[]bytes, int p ,int length) {
-        ensure(regex.codeLength + length);
+        ensure(codeLength + length);
         int end = p + length;
-        
-        while (p < end) regex.code[regex.codeLength++] = bytes[p++];
+
+        while (p < end) code[codeLength++] = bytes[p++];
     }
-    
-    private void addInts(int[]ints, int length) { 
-        ensure(regex.codeLength + length);
-        System.arraycopy(ints, 0, regex.code, regex.codeLength, length);
-        regex.codeLength += length;
+
+    private void addInts(int[]ints, int length) {
+        ensure(codeLength + length);
+        System.arraycopy(ints, 0, code, codeLength, length);
+        codeLength += length;
     }
-    
+
     private void addOpcode(int opcode) {
         addInt(opcode);
-        
+
         switch(opcode) {
         case OPCode.ANYCHAR_STAR:
         case OPCode.ANYCHAR_STAR_SB:
@@ -1165,11 +1190,11 @@ final class ArrayCompiler extends Compiler {
         case OPCode.ANYCHAR_ML_STAR_SB:
         case OPCode.ANYCHAR_STAR_PEEK_NEXT:
         case OPCode.ANYCHAR_STAR_PEEK_NEXT_SB:
-        case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT:            
+        case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT:
         case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB:
         case OPCode.STATE_CHECK_ANYCHAR_STAR:
         case OPCode.STATE_CHECK_ANYCHAR_STAR_SB:
-        case OPCode.STATE_CHECK_ANYCHAR_ML_STAR:            
+        case OPCode.STATE_CHECK_ANYCHAR_ML_STAR:
         case OPCode.MEMORY_START_PUSH:
         case OPCode.MEMORY_END_PUSH:
         case OPCode.MEMORY_END_PUSH_REC:
@@ -1187,7 +1212,7 @@ final class ArrayCompiler extends Compiler {
         case OPCode.REPEAT_INC_SG:
         case OPCode.REPEAT_INC_NG:
         case OPCode.REPEAT_INC_NG_SG:
-        case OPCode.PUSH_POS:            
+        case OPCode.PUSH_POS:
         case OPCode.PUSH_POS_NOT:
         case OPCode.PUSH_STOP_BT:
         case OPCode.PUSH_LOOK_BEHIND_NOT:
@@ -1233,5 +1258,16 @@ final class ArrayCompiler extends Compiler {
     private void addOpcodeOption(int opcode, int option) {
         addOpcode(opcode);
         addOption(option);
-    }    
+    }
+
+    private void addTemplate(byte[]bytes) {
+        if (templateNum == 0) {
+            templates = new byte[2][];
+        } else if (templateNum == templates.length) {
+            byte[][]tmp = new byte[templateNum * 2][];
+            System.arraycopy(templates, 0, tmp, 0, templateNum);
+            templates = tmp;
+        }
+        templates[templateNum++] = bytes;
+    }
 }
diff --git a/src/org/joni/AsmCompiler.java b/src/org/joni/AsmCompiler.java
index 433d324..e39dc05 100644
--- a/src/org/joni/AsmCompiler.java
+++ b/src/org/joni/AsmCompiler.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -33,7 +33,7 @@ final class AsmCompiler extends AsmCompilerSupport {
     public AsmCompiler(Analyser analyser) {
         super(analyser);
     }
-    
+
     @Override
     protected void prepare() {
         REG_NUM++;
@@ -44,7 +44,7 @@ final class AsmCompiler extends AsmCompilerSupport {
         prepareFactory();
         prepareFactoryInit();
     }
-    
+
     @Override
     protected void finish() {
         setupFactoryInit();
diff --git a/src/org/joni/AsmCompilerSupport.java b/src/org/joni/AsmCompilerSupport.java
index 217aa8b..9f98015 100644
--- a/src/org/joni/AsmCompilerSupport.java
+++ b/src/org/joni/AsmCompilerSupport.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -32,7 +32,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
     protected MethodVisitor factoryInit;// factory constructor
     protected String factoryName;
 
-    protected ClassWriter machine;      // matcher    
+    protected ClassWriter machine;      // matcher
     protected MethodVisitor machineInit;// matcher constructor
     protected MethodVisitor match;      // actual matcher implementation (the matchAt method)
     protected String machineName;
@@ -40,7 +40,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
     // we will? try to manage visitMaxs ourselves for efficiency
     protected int maxStack = 1;
     protected int maxVars = LAST_INDEX;
-    
+
     // for field generation
     protected int bitsets, ranges, templates;
 
@@ -54,7 +54,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
         }
     };
 
-    private static final DummyClassLoader loader = new DummyClassLoader(); 
+    private static final DummyClassLoader loader = new DummyClassLoader();
 
     AsmCompilerSupport(Analyser analyser) {
         super(analyser);
@@ -97,7 +97,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
         machine = new ClassWriter(ClassWriter.COMPUTE_MAXS);
         machineName = "org/joni/NativeMachine" + REG_NUM;
     }
-    
+
     protected final void prepareMachineInit() {
         machine.visit(V1_4, ACC_PUBLIC + ACC_FINAL, machineName, null, "org/joni/NativeMachine", null);
         machineInit = machine.visitMethod(ACC_PROTECTED, "<init>", "(Lorg/joni/Regex;[BII)V", null, null);
@@ -108,7 +108,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
         machineInit.visitVarInsn(ILOAD, 4);     // end
         machineInit.visitMethodInsn(INVOKESPECIAL, "org/joni/NativeMachine", "<init>", "(Lorg/joni/Regex;[BII)V");
     }
-    
+
     protected final void setupMachineInit() {
         if (bitsets + ranges + templates > 0) { // ok, some of these are in use, we'd like to cache the factory
             machine.visitField(ACC_PRIVATE + ACC_FINAL, "factory", "L" + factoryName + ";", null, null);
@@ -129,7 +129,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
         match = machine.visitMethod(ACC_SYNTHETIC, "matchAt", "(III)I", null, null);
         move(S, SSTART);        // s = sstart
         load("bytes", "[B");    //
-        astore(BYTES);          // byte[]bytes = this.bytes 
+        astore(BYTES);          // byte[]bytes = this.bytes
     }
 
     protected final void setupMachineMatch() {
@@ -232,7 +232,7 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
         factory.visitField(ACC_PRIVATE + ACC_FINAL, name, "[I", null, null);
         factoryInit.visitVarInsn(ALOAD, THIS);          // this;
         loadInt(factoryInit, arr.length);               // this, length
-        factoryInit.visitIntInsn(NEWARRAY, T_INT);      // this, arr        
+        factoryInit.visitIntInsn(NEWARRAY, T_INT);      // this, arr
         for (int i=0;i < arr.length; i++) buildArray(i, arr[i], IASTORE);
         factoryInit.visitFieldInsn(PUTFIELD, factoryName, name, "[I");
     }
@@ -241,13 +241,13 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
         factory.visitField(ACC_PRIVATE + ACC_FINAL, name, "[B", null, null);
         factoryInit.visitVarInsn(ALOAD, THIS);          // this;
         loadInt(factoryInit, arr.length);               // this, length
-        factoryInit.visitIntInsn(NEWARRAY, T_BYTE);     // this, arr        
+        factoryInit.visitIntInsn(NEWARRAY, T_BYTE);     // this, arr
         for (int i=p, j=0; i < p + length; i++, j++) buildArray(j, arr[i] & 0xff, BASTORE);
         factoryInit.visitFieldInsn(PUTFIELD, factoryName, name, "[B");
     }
 
     private void buildArray(int index, int value, int type) {
-        factoryInit.visitInsn(DUP);     // ... arr, arr        
+        factoryInit.visitInsn(DUP);     // ... arr, arr
         loadInt(factoryInit, index);    // ... arr, arr, index
         loadInt(factoryInit, value);    // ... arr, arr, index, value
         factoryInit.visitInsn(type);    // ... arr
@@ -255,13 +255,13 @@ abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConsta
 
     private void loadInt(MethodVisitor mv, int value) {
         if (value >= -1 && value <= 5) {
-            mv.visitInsn(value + ICONST_0); // ICONST_0 == 3 
+            mv.visitInsn(value + ICONST_0); // ICONST_0 == 3
         } else if (value >= 6 && value <= 127 || value >= -128 && value <= -2) {
             mv.visitIntInsn(BIPUSH, value);
         } else if (value >= 128 && value <= 32767 || value >= -32768 && value <= -129) {
             mv.visitIntInsn(SIPUSH, value);
         } else {
-            mv.visitLdcInsn(new Integer(value));                
+            mv.visitLdcInsn(new Integer(value));
         }
     }
 }
diff --git a/src/org/joni/BitSet.java b/src/org/joni/BitSet.java
index 3d9cf99..b58d5eb 100644
--- a/src/org/joni/BitSet.java
+++ b/src/org/joni/BitSet.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -25,7 +25,7 @@ public final class BitSet {
     private static final int BITS_IN_ROOM = 4 * BITS_PER_BYTE;
     static final int BITSET_SIZE = (SINGLE_BYTE_SIZE / BITS_IN_ROOM);
     static final int ROOM_SHIFT = log2(BITS_IN_ROOM);
-    
+
     final int[] bits = new int[BITSET_SIZE];
 
     private static final int BITS_TO_STRING_WRAP = 4;
@@ -38,23 +38,23 @@ public final class BitSet {
         }
         return buffer.toString();
     }
-    
+
     public boolean at(int pos) {
         return (bits[pos >>> ROOM_SHIFT] & bit(pos)) != 0;
     }
 
     public void set(int pos) {
-        bits[pos >>> ROOM_SHIFT] |= bit(pos); 
+        bits[pos >>> ROOM_SHIFT] |= bit(pos);
     }
 
     public void clear(int pos) {
-        bits[pos >>> ROOM_SHIFT] &= ~bit(pos); 
+        bits[pos >>> ROOM_SHIFT] &= ~bit(pos);
     }
 
     public void invert(int pos) {
         bits[pos >>> ROOM_SHIFT] ^= bit(pos);
     }
-    
+
     public void clear() {
         for (int i=0; i<BITSET_SIZE; i++) bits[i]=0;
     }
@@ -69,11 +69,11 @@ public final class BitSet {
     public void setRange(int from, int to) {
         for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) set(i);
     }
-    
+
     public void setAll() {
         for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~0;
     }
-    
+
     public void invert() {
         for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~bits[i];
     }
@@ -101,11 +101,11 @@ public final class BitSet {
         }
         return num;
     }
-    
+
     static int bit(int pos){
         return 1 << (pos % SINGLE_BYTE_SIZE);
     }
-    
+
     private static int log2(int n){
         int log = 0;
         while ((n >>>= 1) != 0) log++;
diff --git a/src/org/joni/BitStatus.java b/src/org/joni/BitStatus.java
index 1440170..0c68616 100644
--- a/src/org/joni/BitStatus.java
+++ b/src/org/joni/BitStatus.java
@@ -1,27 +1,27 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
 
 final class BitStatus {
     public static final int BIT_STATUS_BITS_NUM = 4 * 8;
-    
+
     public static int bsClear() {
         return 0;
     }
@@ -29,9 +29,9 @@ final class BitStatus {
         return -1;
     }
     public static boolean bsAt(int stats, int n) {
-        return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0; 
+        return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0;
     }
-    public static int bsOnAt(int stats, int n) {    
+    public static int bsOnAt(int stats, int n) {
         if (n < BIT_STATUS_BITS_NUM) {
             stats |= (1 << n);
         } else {
@@ -43,7 +43,7 @@ final class BitStatus {
         if (n < BIT_STATUS_BITS_NUM) stats |= (1 << n);
         return stats;
     }
-    
+
     public static int bsOnOff(int v, int f, boolean negative) {
         if (negative) {
             v &= ~f;
diff --git a/src/org/joni/ByteCodeMachine.java b/src/org/joni/ByteCodeMachine.java
index 9ca1988..9056166 100644
--- a/src/org/joni/ByteCodeMachine.java
+++ b/src/org/joni/ByteCodeMachine.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -40,7 +40,7 @@ class ByteCodeMachine extends StackMachine {
     private int bestLen;          // return value
     private int s = 0;            // current char
 
-    private int range;            // right range    
+    private int range;            // right range
     private int sprev;
     private int sstart;
     private int sbegin;
@@ -58,7 +58,7 @@ class ByteCodeMachine extends StackMachine {
         //CaptureTreeNode child;
         int k = stkp;
         //int k = kp;
-        
+
         while (k < stk) {
             StackEntry e = stack[k];
             if (e.type == MEM_START) {
@@ -70,7 +70,7 @@ class ByteCodeMachine extends StackMachine {
                     node.addChild(child);
                     stkp = k + 1;
                     if (makeCaptureHistoryTree(child)) return true;
-                    
+
                     k = stkp;
                     child.end = e.getMemPStr() - str;
                 }
@@ -93,12 +93,12 @@ class ByteCodeMachine extends StackMachine {
             node = region.historyRoot;
             node.clear();
         }
-        
+
         // was clear ???
         node.group = 0;
         node.beg = sstart - str;
         node.end = s      - str;
-        
+
         stkp = 0;
         makeCaptureHistoryTree(region.historyRoot);
     }
@@ -113,11 +113,11 @@ class ByteCodeMachine extends StackMachine {
     protected final byte[]cfbuf2() {
         return cfbuf2 == null ? cfbuf2 = new byte[Config.ENC_MBC_CASE_FOLD_MAXLEN] : cfbuf2;
     }
-    
+
     private boolean stringCmpIC(int caseFlodFlag, int s1, IntHolder ps2, int mbLen, int textEnd) {
         byte[]buf1 = cfbuf();
         byte[]buf2 = cfbuf2();
-        
+
         int s2 = ps2.value;
         int end1 = s1 + mbLen;
 
@@ -128,11 +128,11 @@ class ByteCodeMachine extends StackMachine {
             value = s2;
             int len2 = enc.mbcCaseFold(caseFlodFlag, bytes, this, textEnd, buf2);
             s2 = value;
-            
+
             if (len1 != len2) return false;
             int p1 = 0;
             int p2 = 0;
-            
+
             while (len1-- > 0) {
                 if (buf1[p1] != buf2[p2]) return false;
                 p1++; p2++;
@@ -141,16 +141,16 @@ class ByteCodeMachine extends StackMachine {
         ps2.value = s2;
         return true;
     }
-    
+
     private void debugMatchBegin() {
         Config.log.println("match_at: " +
                 "str: " + str +
                 ", end: " + end +
-                ", start: " + this.sstart + 
+                ", start: " + this.sstart +
                 ", sprev: " + this.sprev);
         Config.log.println("size: " + (end - str) + ", start offset: " + (this.sstart - str));
     }
-    
+
     private void debugMatchLoop() {
         if (Config.DEBUG_MATCH) {
             Config.log.printf("%4d", (s - str)).print("> \"");
@@ -166,28 +166,28 @@ class ByteCodeMachine extends StackMachine {
             StringBuilder sb = new StringBuilder();
             new ByteCodePrinter(regex).compiledByteCodeToString(sb, ip);
             Config.log.println(sb.toString());
-        }        
+        }
     }
-    
+
     protected final int matchAt(int range, int sstart, int sprev) {
         this.range = range;
         this.sstart = sstart;
         this.sprev = sprev;
-        
+
         stk = 0;
         ip = 0;
-        
+
         if (Config.DEBUG_MATCH) debugMatchBegin();
-        
+
         init();
-        
+
         bestLen = -1;
         s = sstart;
 
         final int[]code = this.code;
         while (true) {
             if (Config.DEBUG_MATCH) debugMatchLoop();
-            
+
             sbegin = s;
             switch (code[ip++]) {
                 case OPCode.END:    if (opEnd()) return finish();                  break;
@@ -215,7 +215,7 @@ class ByteCodeMachine extends StackMachine {
                 case OPCode.CCLASS_MB_NOT:              opCClassMBNot();           break;
                 case OPCode.CCLASS_MIX_NOT:             opCClassMIXNot();          break;
                 case OPCode.CCLASS_NODE:                opCClassNode();            break;
-                
+
                 case OPCode.ANYCHAR:                    opAnyChar();               break;
                 case OPCode.ANYCHAR_ML:                 opAnyCharML();             break;
                 case OPCode.ANYCHAR_STAR:               opAnyCharStar();           break;
@@ -224,28 +224,28 @@ class ByteCodeMachine extends StackMachine {
                 case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT:  opAnyCharMLStarPeekNext(); break;
                 case OPCode.STATE_CHECK_ANYCHAR_STAR:   opStateCheckAnyCharStar(); break;
                 case OPCode.STATE_CHECK_ANYCHAR_ML_STAR:opStateCheckAnyCharMLStar();break;
-                
+
                 case OPCode.WORD:                       opWord();                  break;
                 case OPCode.NOT_WORD:                   opNotWord();               break;
                 case OPCode.WORD_BOUND:                 opWordBound();             continue;
                 case OPCode.NOT_WORD_BOUND:             opNotWordBound();          continue;
                 case OPCode.WORD_BEGIN:                 opWordBegin();             continue;
                 case OPCode.WORD_END:                   opWordEnd();               continue;
-                
+
                 case OPCode.BEGIN_BUF:                  opBeginBuf();              continue;
                 case OPCode.END_BUF:                    opEndBuf();                continue;
                 case OPCode.BEGIN_LINE:                 opBeginLine();             continue;
                 case OPCode.END_LINE:                   opEndLine();               continue;
                 case OPCode.SEMI_END_BUF:               opSemiEndBuf();            continue;
                 case OPCode.BEGIN_POSITION:             opBeginPosition();         continue;
-                
+
                 case OPCode.MEMORY_START_PUSH:          opMemoryStartPush();       continue;
                 case OPCode.MEMORY_START:               opMemoryStart();           continue;
                 case OPCode.MEMORY_END_PUSH:            opMemoryEndPush();         continue;
                 case OPCode.MEMORY_END:                 opMemoryEnd();             continue;
                 case OPCode.MEMORY_END_PUSH_REC:        opMemoryEndPushRec();      continue;
                 case OPCode.MEMORY_END_REC:             opMemoryEndRec();          continue;
-                
+
                 case OPCode.BACKREF1:                   opBackRef1();              continue;
                 case OPCode.BACKREF2:                   opBackRef2();              continue;
                 case OPCode.BACKREFN:                   opBackRefN();              continue;
@@ -253,50 +253,50 @@ class ByteCodeMachine extends StackMachine {
                 case OPCode.BACKREF_MULTI:              opBackRefMulti();          continue;
                 case OPCode.BACKREF_MULTI_IC:           opBackRefMultiIC();        continue;
                 case OPCode.BACKREF_WITH_LEVEL:         opBackRefAtLevel();        continue;
-                
+
                 case OPCode.NULL_CHECK_START:           opNullCheckStart();        continue;
                 case OPCode.NULL_CHECK_END:             opNullCheckEnd();          continue;
                 case OPCode.NULL_CHECK_END_MEMST:       opNullCheckEndMemST();     continue;
                 case OPCode.NULL_CHECK_END_MEMST_PUSH:  opNullCheckEndMemSTPush(); continue;
-                    
+
                 case OPCode.JUMP:                       opJump();                  continue;
                 case OPCode.PUSH:                       opPush();                  continue;
-                
+
                 // CEC
                 case OPCode.STATE_CHECK_PUSH:           opStateCheckPush();        continue;
                 case OPCode.STATE_CHECK_PUSH_OR_JUMP:   opStateCheckPushOrJump();  continue;
                 case OPCode.STATE_CHECK:                opStateCheck();            continue;
-                    
+
                 case OPCode.POP:                        opPop();                   continue;
                 case OPCode.PUSH_OR_JUMP_EXACT1:        opPushOrJumpExact1();      continue;
                 case OPCode.PUSH_IF_PEEK_NEXT:          opPushIfPeekNext();        continue;
-                
+
                 case OPCode.REPEAT:                     opRepeat();                continue;
                 case OPCode.REPEAT_NG:                  opRepeatNG();              continue;
                 case OPCode.REPEAT_INC:                 opRepeatInc();             continue;
                 case OPCode.REPEAT_INC_SG:              opRepeatIncSG();           continue;
                 case OPCode.REPEAT_INC_NG:              opRepeatIncNG();           continue;
                 case OPCode.REPEAT_INC_NG_SG:           opRepeatIncNGSG();         continue;
-                
+
                 case OPCode.PUSH_POS:                   opPushPos();               continue;
                 case OPCode.POP_POS:                    opPopPos();                continue;
                 case OPCode.PUSH_POS_NOT:               opPushPosNot();            continue;
                 case OPCode.FAIL_POS:                   opFailPos();               continue;
                 case OPCode.PUSH_STOP_BT:               opPushStopBT();            continue;
                 case OPCode.POP_STOP_BT:                opPopStopBT();             continue;
-                
+
                 case OPCode.LOOK_BEHIND:                opLookBehind();            continue;
                 case OPCode.PUSH_LOOK_BEHIND_NOT:       opPushLookBehindNot();     continue;
                 case OPCode.FAIL_LOOK_BEHIND_NOT:       opFailLookBehindNot();     continue;
-                
+
                 // USE_SUBEXP_CALL
                 case OPCode.CALL:                       opCall();                  continue;
                 case OPCode.RETURN:                     opReturn();                continue;
-                
+
                 // single byte implementations
                 case OPCode.CCLASS_SB:                      opCClassSb();                break;
-                case OPCode.CCLASS_NOT_SB:                  opCClassNotSb();             break;                
-                
+                case OPCode.CCLASS_NOT_SB:                  opCClassNotSb();             break;
+
                 case OPCode.ANYCHAR_SB:                     opAnyCharSb();               break;
                 case OPCode.ANYCHAR_ML_SB:                  opAnyCharMLSb();             break;
                 case OPCode.ANYCHAR_STAR_SB:                opAnyCharStarSb();           break;
@@ -305,14 +305,14 @@ class ByteCodeMachine extends StackMachine {
                 case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB:   opAnyCharMLStarPeekNextSb(); break;
                 case OPCode.STATE_CHECK_ANYCHAR_STAR_SB:    opStateCheckAnyCharStarSb(); break;
                 case OPCode.STATE_CHECK_ANYCHAR_ML_STAR_SB: opStateCheckAnyCharMLStarSb();break;
-                
+
                 case OPCode.WORD_SB:                        opWordSb();                  break;
                 case OPCode.NOT_WORD_SB:                    opNotWordSb();               break;
                 case OPCode.WORD_BOUND_SB:                  opWordBoundSb();             continue;
                 case OPCode.NOT_WORD_BOUND_SB:              opNotWordBoundSb();          continue;
                 case OPCode.WORD_BEGIN_SB:                  opWordBeginSb();             continue;
                 case OPCode.WORD_END_SB:                    opWordEndSb();               continue;
-                    
+
                 case OPCode.LOOK_BEHIND_SB:                 opLookBehindSb();            continue;
 
                 case OPCode.EXACT1_IC_SB:                   opExact1ICSb();              break;
@@ -320,19 +320,19 @@ class ByteCodeMachine extends StackMachine {
 
                 case OPCode.FINISH:
                     return finish();
-                
+
                 case OPCode.FAIL:                       opFail();                  continue;
-                    
+
                 default:
                     throw new InternalException(ErrorMessages.ERR_UNDEFINED_BYTECODE);
-            
+
             } // main switch
         } // main while
     }
 
     private boolean opEnd() {
         int n = s - sstart;
-        
+
         if (n > bestLen) {
             if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
                 if (isFindLongest(regex.options)) {
@@ -345,7 +345,7 @@ class ByteCodeMachine extends StackMachine {
                     }
                 }
             } // USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
-            
+
             bestLen = n;
             final Region region = msaRegion;
             if (region != null) {
@@ -357,25 +357,25 @@ class ByteCodeMachine extends StackMachine {
                     if (repeatStk[memEndStk + i] != INVALID_INDEX) {
                         region.beg[i] = bsAt(regex.btMemStart, i) ?
                                         stack[repeatStk[memStartStk + i]].getMemPStr() - str :
-                                        repeatStk[memStartStk + i] - str;    
-                        
-                        
+                                        repeatStk[memStartStk + i] - str;
+
+
                         region.end[i] = bsAt(regex.btMemEnd, i) ?
                                         stack[repeatStk[memEndStk + i]].getMemPStr() :
                                         repeatStk[memEndStk + i] - str;
-                        
+
                     } else {
                         region.beg[i] = region.end[i] = Region.REGION_NOTPOS;
                     }
-                    
+
                 }
-                
+
                 if (Config.USE_CAPTURE_HISTORY) {
                     if (regex.captureHistory != 0) checkCaptureHistory(region);
                 }
             } else {
                 msaBegin = sstart - str;
-                msaEnd   = s      - str; 
+                msaEnd   = s      - str;
             }
         } else {
             Region region = msaRegion;
@@ -393,13 +393,13 @@ class ByteCodeMachine extends StackMachine {
                 } else {
                     msaBegin = msaEnd = 0;
                 }
-            } // USE_POSIX_REGION_OPTION            
+            } // USE_POSIX_REGION_OPTION
         }
         // end_best_len:
         /* default behavior: return first-matching result. */
         return endBestLength();
     }
-    
+
     private boolean endBestLength() {
         if (isFindCondition(regex.options)) {
             if (isFindNotEmpty(regex.options) && s == sstart) {
@@ -416,11 +416,11 @@ class ByteCodeMachine extends StackMachine {
 
     private void opExact1() {
         if (s >= range || code[ip] != bytes[s++]) {opFail(); return;}
-        //if (s > range) {opFail(); return;} 
+        //if (s > range) {opFail(); return;}
         ip++;
         sprev = sbegin; // break;
     }
-    
+
     private void opExact2() {
         if (s + 2 > range) {opFail(); return;}
         if (code[ip] != bytes[s]) {opFail(); return;}
@@ -440,7 +440,7 @@ class ByteCodeMachine extends StackMachine {
         sprev = s;
         ip++; s++;
     }
-    
+
     private void opExact4() {
         if (s + 4 > range) {opFail(); return;}
         if (code[ip] != bytes[s]) {opFail(); return;}
@@ -453,7 +453,7 @@ class ByteCodeMachine extends StackMachine {
         sprev = s;
         ip++; s++;
     }
-    
+
     private void opExact5() {
         if (s + 5 > range) {opFail(); return;}
         if (code[ip] != bytes[s]) {opFail(); return;}
@@ -468,12 +468,20 @@ class ByteCodeMachine extends StackMachine {
         sprev = s;
         ip++; s++;
     }
-    
+
     private void opExactN() {
-        int tlen = code[ip++];        
+        int tlen = code[ip++];
         if (s + tlen > range) {opFail(); return;}
-        
-        while (tlen-- > 0) if (code[ip++] != bytes[s++]) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            byte[]bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while (tlen-- > 0) if (bs[ps++] != bytes[s++]) {opFail(); return;}
+
+        } else {
+            while (tlen-- > 0) if (code[ip++] != bytes[s++]) {opFail(); return;}
+        }
         sprev = s - 1;
     }
 
@@ -485,7 +493,7 @@ class ByteCodeMachine extends StackMachine {
         ip++; s++;
         sprev = sbegin; // break;
     }
-    
+
     private void opExactMB2N2() {
         if (s + 4 > range) {opFail(); return;}
         if (code[ip] != bytes[s]) {opFail(); return;}
@@ -498,7 +506,7 @@ class ByteCodeMachine extends StackMachine {
         if (code[ip] != bytes[s]) {opFail(); return;}
         ip++; s++;
    }
-    
+
     private void opExactMB2N3() {
         if (s + 6 > range) {opFail(); return;}
         if (code[ip] != bytes[s]) {opFail(); return;}
@@ -515,58 +523,96 @@ class ByteCodeMachine extends StackMachine {
         if (code[ip] != bytes[s]) {opFail(); return;}
         ip++; s++;
     }
-    
-    private void opExactMB2N() { 
+
+    private void opExactMB2N() {
         int tlen = code[ip++];
-        if (tlen * 2 > range) {opFail(); return;}
-        
-        while(tlen-- > 0) {
-            if (code[ip] != bytes[s]) {opFail(); return;}
-            ip++; s++;
-            if (code[ip] != bytes[s]) {opFail(); return;}
-            ip++; s++;
+        if (s + tlen * 2 > range) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            byte[]bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while(tlen-- > 0) {
+                if (bs[ps] != bytes[s]) {opFail(); return;}
+                ps++; s++;
+                if (bs[ps] != bytes[s]) {opFail(); return;}
+                ps++; s++;
+            }
+        } else {
+            while(tlen-- > 0) {
+                if (code[ip] != bytes[s]) {opFail(); return;}
+                ip++; s++;
+                if (code[ip] != bytes[s]) {opFail(); return;}
+                ip++; s++;
+            }
         }
         sprev = s - 2;
     }
-    
+
     private void opExactMB3N() {
         int tlen = code[ip++];
-        if (tlen * 3 > range) {opFail(); return;}
-        
-        while (tlen-- > 0) {
-            if (code[ip] != bytes[s]) {opFail(); return;}
-            ip++; s++;
-            if (code[ip] != bytes[s]) {opFail(); return;}
-            ip++; s++;
-            if (code[ip] != bytes[s]) {opFail(); return;}
-            ip++; s++;
+        if (s + tlen * 3 > range) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            byte[]bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while (tlen-- > 0) {
+                if (bs[ps] != bytes[s]) {opFail(); return;}
+                ps++; s++;
+                if (bs[ps] != bytes[s]) {opFail(); return;}
+                ps++; s++;
+                if (bs[ps] != bytes[s]) {opFail(); return;}
+                ps++; s++;
+            }
+        } else {
+            while (tlen-- > 0) {
+                if (code[ip] != bytes[s]) {opFail(); return;}
+                ip++; s++;
+                if (code[ip] != bytes[s]) {opFail(); return;}
+                ip++; s++;
+                if (code[ip] != bytes[s]) {opFail(); return;}
+                ip++; s++;
+            }
         }
+
         sprev = s - 3;
     }
-    
+
     private void opExactMBN() {
         int tlen = code[ip++];   /* mb-len */
         int tlen2= code[ip++];   /* string len */
-        
+
         tlen2 *= tlen;
         if (s + tlen2 > range) {opFail(); return;}
-        
-        while(tlen2-- > 0) {
-            if (code[ip] != bytes[s]) {opFail(); return;}
-            ip++; s++;
+
+        if (Config.USE_STRING_TEMPLATES) {
+            byte[]bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while (tlen2-- > 0) {
+                if (bs[ps] != bytes[s]) {opFail(); return;}
+                ps++; s++;
+            }
+        } else {
+            while (tlen2-- > 0) {
+                if (code[ip] != bytes[s]) {opFail(); return;}
+                ip++; s++;
+            }
         }
+
         sprev = s - tlen;
     }
-    
+
     private void opExact1IC() {
         if (s >= range) {opFail(); return;}
-        
+
         byte[]lowbuf = cfbuf();
-        
+
         value = s;
         int len = enc.mbcCaseFold(regex.caseFoldFlag, bytes, this, end, lowbuf);
         s = value;
-        
+
         if (s > range) {opFail(); return;}
 
         int q = 0;
@@ -576,7 +622,7 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opExact1ICSb() {
         if (s >= range || code[ip] != enc.toLowerCaseTable()[bytes[s++] & 0xff]) {opFail(); return;}
         ip++;
@@ -585,40 +631,73 @@ class ByteCodeMachine extends StackMachine {
 
     private void opExactNIC() {
         int tlen = code[ip++];
-        int endp = ip + tlen;
-        
         byte[]lowbuf = cfbuf();
-        
-        while (ip < endp) {
-            sprev = s;
-            if (s >= range) {opFail(); return;}
-            
-            value = s;
-            int len = enc.mbcCaseFold(regex.caseFoldFlag, bytes, this, end, lowbuf);
-            s = value;
-            
-            if (s > range) {opFail(); return;}
-            int q = 0;
-            while (len-- > 0) { 
-                if (code[ip] != lowbuf[q]) {opFail(); return;}
-                ip++; q++;
+
+        if (Config.USE_STRING_TEMPLATES) {
+            byte[]bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+            int endp = ps + tlen;
+
+            while (ps < endp) {
+                sprev = s;
+                if (s >= range) {opFail(); return;}
+
+                value = s;
+                int len = enc.mbcCaseFold(regex.caseFoldFlag, bytes, this, end, lowbuf);
+                s = value;
+
+                if (s > range) {opFail(); return;}
+                int q = 0;
+                while (len-- > 0) {
+                    if (bs[ps] != lowbuf[q]) {opFail(); return;}
+                    ps++; q++;
+                }
+            }
+        } else {
+            int endp = ip + tlen;
+
+            while (ip < endp) {
+                sprev = s;
+                if (s >= range) {opFail(); return;}
+
+                value = s;
+                int len = enc.mbcCaseFold(regex.caseFoldFlag, bytes, this, end, lowbuf);
+                s = value;
+
+                if (s > range) {opFail(); return;}
+                int q = 0;
+                while (len-- > 0) {
+                    if (code[ip] != lowbuf[q]) {opFail(); return;}
+                    ip++; q++;
+                }
             }
         }
+
     }
-    
+
     private void opExactNICSb() {
-        int tlen = code[ip++];        
+        int tlen = code[ip++];
         if (s + tlen > range) {opFail(); return;}
-        byte[]toLowerTable = enc.toLowerCaseTable();
-        while (tlen-- > 0) if (code[ip++] != toLowerTable[bytes[s++] & 0xff]) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            byte[]bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+            byte[]toLowerTable = enc.toLowerCaseTable();
+
+            while (tlen-- > 0) if (bs[ps++] != toLowerTable[bytes[s++] & 0xff]) {opFail(); return;}
+        } else {
+            byte[]toLowerTable = enc.toLowerCaseTable();
+
+            while (tlen-- > 0) if (code[ip++] != toLowerTable[bytes[s++] & 0xff]) {opFail(); return;}
+        }
         sprev = s - 1;
-    }    
-    
+    }
+
     private boolean isInBitSet() {
         int c = bytes[s] & 0xff;
         return ((code[ip + (c >>> BitSet.ROOM_SHIFT)] & (1 << c)) != 0);
     }
-    
+
     private void opCClass() {
         if (s >= range || !isInBitSet()) {opFail(); return;}
         ip += BitSet.BITSET_SIZE;
@@ -626,16 +705,16 @@ class ByteCodeMachine extends StackMachine {
         if (s > end) s = end;
         sprev = sbegin; // break;
     }
-    
+
     private void opCClassSb() {
         if (s >= range || !isInBitSet()) {opFail(); return;}
         ip += BitSet.BITSET_SIZE;
         s++;
         sprev = sbegin; // break;
-    }    
-    
+    }
+
     private boolean isInClassMB() {
-        int tlen = code[ip++];        
+        int tlen = code[ip++];
         if (s >= range) return false;
         int mbLen = enc.length(bytes, s, end);
         if (s + mbLen > range) return false;
@@ -644,16 +723,16 @@ class ByteCodeMachine extends StackMachine {
         int c = enc.mbcToCode(bytes, ss, s);
         if (!CodeRange.isInCodeRange(code, ip, c)) return false;
         ip += tlen;
-        return true;        
+        return true;
     }
-    
+
     private void opCClassMB() {
-        // beyond string check 
+        // beyond string check
         if (s >= range || !enc.isMbcHead(bytes, s, end)) {opFail(); return;}
         if (!isInClassMB()) {opFail(); return;} // not!!!
         sprev = sbegin; // break;
     }
-    
+
     private void opCClassMIX() {
         if (s >= range) {opFail(); return;}
         if (enc.isMbcHead(bytes, s, end)) {
@@ -668,7 +747,7 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opCClassNot() {
         if (s >= range || isInBitSet()) {opFail(); return;}
         ip += BitSet.BITSET_SIZE;
@@ -676,34 +755,34 @@ class ByteCodeMachine extends StackMachine {
         if (s > end) s = end;
         sprev = sbegin; // break;
     }
-    
+
     private void opCClassNotSb() {
         if (s >= range || isInBitSet()) {opFail(); return;}
         ip += BitSet.BITSET_SIZE;
         s++;
         sprev = sbegin; // break;
-    }    
-    
+    }
+
     private boolean isNotInClassMB() {
         int tlen = code[ip++];
         int mbLen = enc.length(bytes, s, end);
-        
+
         if (!(s + mbLen <= range)) {
             if (s >= range) return false;
             s = end;
             ip += tlen;
             return true;
         }
-        
+
         int ss = s;
         s += mbLen;
         int c = enc.mbcToCode(bytes, ss, s);
-        
+
         if (CodeRange.isInCodeRange(code, ip, c)) return false;
         ip += tlen;
         return true;
-    }    
-    
+    }
+
     private void opCClassMBNot() {
         if (s >= range) {opFail(); return;}
         if (!enc.isMbcHead(bytes, s, end)) {
@@ -716,7 +795,7 @@ class ByteCodeMachine extends StackMachine {
         if (!isNotInClassMB()) {opFail(); return;}
         sprev = sbegin; // break;
     }
-    
+
     private void opCClassMIXNot() {
         if (s >= range) {opFail(); return;}
         if (enc.isMbcHead(bytes, s, end)) {
@@ -731,7 +810,7 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opCClassNode() {
         if (s >= range) {opFail(); return;}
         CClassNode cc = (CClassNode)regex.operands[code[ip++]];
@@ -743,7 +822,7 @@ class ByteCodeMachine extends StackMachine {
         if (!cc.isCodeInCCLength(mbLen, c)) {opFail(); return;}
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyChar() {
         if (s >= range) {opFail(); return;}
         int n = enc.length(bytes, s, end);
@@ -752,28 +831,28 @@ class ByteCodeMachine extends StackMachine {
         s += n;
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharSb() {
         if (s >= range) {opFail(); return;}
         if (bytes[s] == Encoding.NEW_LINE) {opFail(); return;}
         s++;
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharML() {
         if (s >= range) {opFail(); return;}
         int n = enc.length(bytes, s, end);
         if (s + n > range) {opFail(); return;}
-        s += n;        
-        sprev = sbegin; // break;        
+        s += n;
+        sprev = sbegin; // break;
     }
 
     private void opAnyCharMLSb() {
         if (s >= range) {opFail(); return;}
         s++;
-        sprev = sbegin; // break;        
+        sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharStar() {
         final byte[]bytes = this.bytes;
         while (s < range) {
@@ -786,7 +865,7 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharStarSb() {
         final byte[]bytes = this.bytes;
         while (s < range) {
@@ -797,7 +876,7 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharMLStar() {
         final byte[]bytes = this.bytes;
         while (s < range) {
@@ -818,11 +897,11 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharStarPeekNext() {
         final byte c = (byte)code[ip];
         final byte[]bytes = this.bytes;
-        
+
         while (s < range) {
             if (c == bytes[s]) pushAlt(ip + 1, s, sprev);
             int n = enc.length(bytes, s, end);
@@ -833,11 +912,11 @@ class ByteCodeMachine extends StackMachine {
         ip++;
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharStarPeekNextSb() {
         final byte c = (byte)code[ip];
         final byte[]bytes = this.bytes;
-        
+
         while (s < range) {
             byte b = bytes[s];
             if (c == b) pushAlt(ip + 1, s, sprev);
@@ -848,11 +927,11 @@ class ByteCodeMachine extends StackMachine {
         ip++;
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharMLStarPeekNext() {
         final byte c = (byte)code[ip];
         final byte[]bytes = this.bytes;
-        
+
         while (s < range) {
             if (c == bytes[s]) pushAlt(ip + 1, s, sprev);
             int n = enc.length(bytes, s, end);
@@ -863,7 +942,7 @@ class ByteCodeMachine extends StackMachine {
         ip++;
         sprev = sbegin; // break;
     }
-    
+
     private void opAnyCharMLStarPeekNextSb() {
         final byte c = (byte)code[ip];
         final byte[]bytes = this.bytes;
@@ -876,12 +955,12 @@ class ByteCodeMachine extends StackMachine {
         ip++;
         sprev = sbegin; // break;
     }
-    
+
     // CEC
     private void opStateCheckAnyCharStar() {
         int mem = code[ip++];
         final byte[]bytes = this.bytes;
-        
+
         while (s < range) {
             if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
@@ -892,11 +971,11 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opStateCheckAnyCharStarSb() {
         int mem = code[ip++];
         final byte[]bytes = this.bytes;
-        
+
         while (s < range) {
             if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
@@ -906,12 +985,12 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     // CEC
     private void opStateCheckAnyCharMLStar() {
         int mem = code[ip++];
-        
-        final byte[]bytes = this.bytes;            
+
+        final byte[]bytes = this.bytes;
         while (s < range) {
             if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
@@ -922,10 +1001,10 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opStateCheckAnyCharMLStarSb() {
         int mem = code[ip++];
-        
+
         while (s < range) {
             if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
@@ -934,7 +1013,7 @@ class ByteCodeMachine extends StackMachine {
         }
         sprev = sbegin; // break;
     }
-    
+
     private void opWord() {
         if (s >= range || !enc.isMbcWord(bytes, s, end)) {opFail(); return;}
         s += enc.length(bytes, s, end);
@@ -943,22 +1022,22 @@ class ByteCodeMachine extends StackMachine {
 
     private void opWordSb() {
         if (s >= range || !enc.isWord(bytes[s] & 0xff)) {opFail(); return;}
-        s++;        
+        s++;
         sprev = sbegin; // break;
     }
-    
+
     private void opNotWord() {
         if (s >= range || enc.isMbcWord(bytes, s, end)) {opFail(); return;}
         s += enc.length(bytes, s, end);
         sprev = sbegin; // break;
     }
-    
+
     private void opNotWordSb() {
         if (s >= range || enc.isWord(bytes[s] & 0xff)) {opFail(); return;}
         s++;
         sprev = sbegin; // break;
     }
-    
+
     private void opWordBound() {
         if (s == str) {
             if (s >= range || !enc.isMbcWord(bytes, s, end)) {opFail(); return;}
@@ -968,7 +1047,7 @@ class ByteCodeMachine extends StackMachine {
             if (enc.isMbcWord(bytes, s, end) == enc.isMbcWord(bytes, sprev, end)) {opFail(); return;}
         }
     }
-    
+
     private void opWordBoundSb() {
         if (s == str) {
             if (s >= range || !enc.isWord(bytes[s] & 0xff)) {opFail(); return;}
@@ -978,7 +1057,7 @@ class ByteCodeMachine extends StackMachine {
             if (enc.isWord(bytes[s] & 0xff) == enc.isWord(bytes[sprev] & 0xff)) {opFail(); return;}
         }
     }
-    
+
     private void opNotWordBound() {
         if (s == str) {
             if (s < range && enc.isMbcWord(bytes, s, end)) {opFail(); return;}
@@ -998,7 +1077,7 @@ class ByteCodeMachine extends StackMachine {
             if (enc.isWord(bytes[s] & 0xff) != enc.isWord(bytes[sprev] & 0xff)) {opFail(); return;}
         }
     }
-    
+
     private void opWordBegin() {
         if (s < range && enc.isMbcWord(bytes, s, end)) {
             if (s == str || !enc.isMbcWord(bytes, sprev, end)) return;
@@ -1012,7 +1091,7 @@ class ByteCodeMachine extends StackMachine {
         }
         opFail();
     }
-    
+
     private void opWordEnd() {
         if (s != str && enc.isMbcWord(bytes, sprev, end)) {
             if (s == end || !enc.isMbcWord(bytes, s, end)) return;
@@ -1026,15 +1105,15 @@ class ByteCodeMachine extends StackMachine {
         }
         opFail();
     }
-    
+
     private void opBeginBuf() {
         if (s != str) opFail();
     }
-    
+
     private void opEndBuf() {
         if (s != end) opFail();
     }
-    
+
     private void opBeginLine() {
         if (s == str) {
             if (isNotBol(msaOptions)) opFail();
@@ -1044,7 +1123,7 @@ class ByteCodeMachine extends StackMachine {
         }
         opFail();
     }
-    
+
     private void opEndLine()  {
         if (s == end) {
             if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
@@ -1061,7 +1140,7 @@ class ByteCodeMachine extends StackMachine {
         }
         opFail();
     }
-    
+
     private void opSemiEndBuf() {
         if (s == end) {
             if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
@@ -1091,162 +1170,162 @@ class ByteCodeMachine extends StackMachine {
         int mem = code[ip++];
         pushMemStart(mem, s);
     }
-    
+
     private void opMemoryStart() {
         int mem = code[ip++];
         repeatStk[memStartStk + mem] = s;
     }
-    
+
     private void opMemoryEndPush() {
         int mem = code[ip++];
         pushMemEnd(mem, s);
     }
-    
+
     private void opMemoryEnd() {
         int mem = code[ip++];
         repeatStk[memEndStk + mem] = s;
     }
-    
+
     private void opMemoryEndPushRec() {
         int mem = code[ip++];
         int stkp = getMemStart(mem); /* should be before push mem-end. */
         pushMemEnd(mem, s);
         repeatStk[memStartStk + mem] = stkp;
     }
-    
+
     private void opMemoryEndRec() {
         int mem = code[ip++];
         repeatStk[memEndStk + mem] = s;
         int stkp = getMemStart(mem);
-        
+
         if (BitStatus.bsAt(regex.btMemStart, mem)) {
             repeatStk[memStartStk + mem] = stkp;
         } else {
             repeatStk[memStartStk + mem] = stack[stkp].getMemPStr();
         }
-        
+
         pushMemEndMark(mem);
     }
-    
+
     private boolean backrefInvalid(int mem) {
         return repeatStk[memEndStk + mem] == INVALID_INDEX || repeatStk[memStartStk + mem] == INVALID_INDEX;
     }
-    
+
     private int backrefStart(int mem) {
         return bsAt(regex.btMemStart, mem) ? stack[repeatStk[memStartStk + mem]].getMemPStr() : repeatStk[memStartStk + mem];
     }
-    
+
     private int backrefEnd(int mem) {
         return bsAt(regex.btMemEnd, mem) ? stack[repeatStk[memEndStk + mem]].getMemPStr() : repeatStk[memEndStk + mem];
     }
-    
+
     private void backref(int mem) {
         /* if you want to remove following line,
         you should check in parse and compile time. (numMem) */
         if (mem > regex.numMem || backrefInvalid(mem)) {opFail(); return;}
-        
+
         int pstart = backrefStart(mem);
         int pend = backrefEnd(mem);
-        
+
         int n = pend - pstart;
         if (s + n > range) {opFail(); return;}
         sprev = s;
-        
+
         // STRING_CMP
         while(n-- > 0) if (bytes[pstart++] != bytes[s++]) {opFail(); return;}
 
         int len;
-        
+
         // beyond string check
         if (sprev < range) {
             while (sprev + (len = enc.length(bytes, sprev, end)) < s) sprev += len;
         }
     }
-    
+
     private void opBackRef1() {
         backref(1);
     }
-    
+
     private void opBackRef2() {
         backref(2);
     }
-    
+
     private void opBackRefN() {
         backref(code[ip++]);
     }
-    
+
     private void opBackRefNIC() {
         int mem = code[ip++];
-        /* if you want to remove following line, 
+        /* if you want to remove following line,
         you should check in parse and compile time. (numMem) */
         if (mem > regex.numMem || backrefInvalid(mem)) {opFail(); return;}
 
         int pstart = backrefStart(mem);
         int pend = backrefEnd(mem);
-        
+
         int n = pend - pstart;
         if (s + n > range) {opFail(); return;}
         sprev = s;
-        
-        value = s;        
+
+        value = s;
         if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end)) {opFail(); return;}
         s = value;
-        
+
         int len;
         // if (sprev < bytes.length)
         while (sprev + (len = enc.length(bytes, sprev, end)) < s) sprev += len;
     }
-    
+
     private void opBackRefMulti() {
         int tlen = code[ip++];
-        
+
         int i;
         loop:for (i=0; i<tlen; i++) {
             int mem = code[ip++];
             if (backrefInvalid(mem)) continue;
-            
+
             int pstart = backrefStart(mem);
             int pend = backrefEnd(mem);
-            
+
             int n = pend - pstart;
             if (s + n > range) {opFail(); return;}
-            
+
             sprev = s;
             int swork = s;
-            
+
             while (n-- > 0) {
                 if (bytes[pstart++] != bytes[swork++]) continue loop;
             }
-            
+
             s = swork;
-            
+
             int len;
 
             // beyond string check
             if (sprev < range) {
                 while (sprev + (len = enc.length(bytes, sprev, end)) < s) sprev += len;
             }
-            
+
             ip += tlen - i  - 1; // * SIZE_MEMNUM (1)
             break; /* success */
         }
         if (i == tlen) {opFail(); return;}
     }
-    
+
     private void opBackRefMultiIC() {
         int tlen = code[ip++];
-        
+
         int i;
         loop:for (i=0; i<tlen; i++) {
             int mem = code[ip++];
             if (backrefInvalid(mem)) continue;
-            
+
             int pstart = backrefStart(mem);
             int pend = backrefEnd(mem);
-            
+
             int n = pend - pstart;
             if (s + n > range) {opFail(); return;}
-            
+
             sprev = s;
 
             value = s;
@@ -1256,13 +1335,13 @@ class ByteCodeMachine extends StackMachine {
             int len;
             // if (sprev < bytes.length)
             while (sprev + (len = enc.length(bytes, sprev, end)) < s) sprev += len;
-            
+
             ip += tlen - i  - 1; // * SIZE_MEMNUM (1)
             break;  /* success */
         }
         if (i == tlen) {opFail(); return;}
     }
-    
+
     private boolean memIsInMemp(int mem, int num, int memp) {
         for (int i=0; i<num; i++) {
             int m = code[memp++];
@@ -1270,9 +1349,9 @@ class ByteCodeMachine extends StackMachine {
         }
         return false;
     }
-    
+
     // USE_BACKREF_AT_LEVEL // (s) and (end) implicit
-    private boolean backrefMatchAtNestedLevel(boolean ignoreCase, int caseFoldFlag, 
+    private boolean backrefMatchAtNestedLevel(boolean ignoreCase, int caseFoldFlag,
                                               int nest, int memNum, int memp) {
         int pend = -1;
         int level = 0;
@@ -1280,7 +1359,7 @@ class ByteCodeMachine extends StackMachine {
 
         while (k >= 0) {
             StackEntry e = stack[k];
-            
+
             if (e.type == CALL_FRAME) {
                 level--;
             } else if (e.type == RETURN) {
@@ -1317,13 +1396,13 @@ class ByteCodeMachine extends StackMachine {
             k--;
         }
         return false;
-    }    
-    
+    }
+
     private void opBackRefAtLevel() {
         int ic      = code[ip++];
         int level   = code[ip++];
         int tlen    = code[ip++];
-        
+
         sprev = s;
         if (backrefMatchAtNestedLevel(ic != 0, regex.caseFoldFlag, level, tlen, ip)) { // (s) and (end) implicit
             int len;
@@ -1349,7 +1428,7 @@ class ByteCodeMachine extends StackMachine {
         int mem = code[ip++];
         pushNullCheckStart(mem, s);
     }
-    
+
     private void nullCheckFound() {
         // null_check_found:
         /* empty loop founded, skip next instruction */
@@ -1366,69 +1445,69 @@ class ByteCodeMachine extends StackMachine {
             break;
         default:
             throw new InternalException(ErrorMessages.ERR_UNEXPECTED_BYTECODE);
-        } // switch        
+        } // switch
     }
-    
+
     private void opNullCheckEnd() {
         int mem = code[ip++];
         int isNull = nullCheck(mem, s); /* mem: null check id */
-        
+
         if (isNull != 0) {
             if (Config.DEBUG_MATCH) {
                 Config.log.println("NULL_CHECK_END: skip  id:" + mem + ", s:" + s);
             }
-            
+
             nullCheckFound();
         }
     }
-    
+
     // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
     private void opNullCheckEndMemST() {
         int mem = code[ip++];   /* mem: null check id */
         int isNull = nullCheckMemSt(mem, s);
-        
+
         if (isNull != 0) {
             if (Config.DEBUG_MATCH) {
                 Config.log.println("NULL_CHECK_END_MEMST: skip  id:" + mem + ", s:" + s);
             }
-            
+
             if (isNull == -1) {opFail(); return;}
             nullCheckFound();
         }
     }
-    
+
     // USE_SUBEXP_CALL
     private void opNullCheckEndMemSTPush() {
         int mem = code[ip++];   /* mem: null check id */
-        
+
         int isNull;
         if (Config.USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT) {
             isNull = nullCheckMemStRec(mem, s);
         } else {
             isNull = nullCheckRec(mem, s);
         }
-        
+
         if (isNull != 0) {
             if (Config.DEBUG_MATCH) {
                 Config.log.println("NULL_CHECK_END_MEMST_PUSH: skip  id:" + mem + ", s:" + s);
-            }            
-            
+            }
+
             if (isNull == -1) {opFail(); return;}
             nullCheckFound();
         } else {
             pushNullCheckEnd(mem);
         }
     }
-    
+
     private void opJump() {
         ip += code[ip] + 1;
     }
-    
+
     private void opPush() {
         int addr = code[ip++];
         pushAlt(ip + addr, s, sprev);
     }
-    
+
     // CEC
     private void opStateCheckPush() {
         int mem = code[ip++];
@@ -1436,43 +1515,44 @@ class ByteCodeMachine extends StackMachine {
         int addr = code[ip++];
         pushAltWithStateCheck(ip + addr, s, sprev, mem);
     }
-    
+
     // CEC
     private void opStateCheckPushOrJump() {
         int mem = code[ip++];
         int addr= code[ip++];
-        
+
         if (stateCheckVal(s, mem)) {
             ip += addr;
         } else {
             pushAltWithStateCheck(ip + addr, s, sprev, mem);
         }
     }
-    
+
     // CEC
     private void opStateCheck() {
         int mem = code[ip++];
         if (stateCheckVal(s, mem)) {opFail(); return;}
         pushStateCheck(s, mem);
     }
-    
+
     private void opPop() {
         popOne();
     }
-    
+
     private void opPushOrJumpExact1() {
         int addr = code[ip++];
-        if (code[ip] == bytes[s] && s < range) {
+        // beyond string check
+        if (s < range && code[ip] == bytes[s]) {
             ip++;
             pushAlt(ip + addr, s, sprev);
             return;
         }
         ip += addr + 1;
     }
-    
+
     private void opPushIfPeekNext() {
         int addr = code[ip++];
-        // beyond string check 
+        // beyond string check
         if (s < range && code[ip] == bytes[s]) {
             ip++;
             pushAlt(ip + addr, s, sprev);
@@ -1480,39 +1560,39 @@ class ByteCodeMachine extends StackMachine {
         }
         ip++;
     }
-    
+
     private void opRepeat() {
         int mem = code[ip++];   /* mem: OP_REPEAT ID */
         int addr= code[ip++];
-        
+
         // ensure1();
         repeatStk[mem] = stk;
         pushRepeat(mem, ip);
-        
+
         if (regex.repeatRangeLo[mem] == 0) { // lower
             pushAlt(ip + addr, s, sprev);
         }
     }
-    
+
     private void opRepeatNG() {
         int mem = code[ip++];   /* mem: OP_REPEAT ID */
         int addr= code[ip++];
-        
+
         // ensure1();
         repeatStk[mem] = stk;
         pushRepeat(mem, ip);
-        
+
         if (regex.repeatRangeLo[mem] == 0) {
             pushAlt(ip, s, sprev);
             ip += addr;
         }
     }
-    
+
     private void repeatInc(int mem, int si) {
         StackEntry e = stack[si];
-        
+
         e.increaseRepeatCount();
-        
+
         if (e.getRepeatCount() >= regex.repeatRangeHi[mem]) {
             /* end of repeat. Nothing to do. */
         } else if (e.getRepeatCount() >= regex.repeatRangeLo[mem]) {
@@ -1523,24 +1603,24 @@ class ByteCodeMachine extends StackMachine {
         }
         pushRepeatInc(si);
     }
-    
+
     private void opRepeatInc() {
-        int mem = code[ip++];   /* mem: OP_REPEAT ID */    
-        int si = repeatStk[mem];        
+        int mem = code[ip++];   /* mem: OP_REPEAT ID */
+        int si = repeatStk[mem];
         repeatInc(mem, si);
     }
-    
+
     private void opRepeatIncSG() {
         int mem = code[ip++];   /* mem: OP_REPEAT ID */
         int si = getRepeat(mem);
         repeatInc(mem, si);
     }
-    
+
     private void repeatIncNG(int mem, int si) {
         StackEntry e = stack[si];
-        
+
         e.increaseRepeatCount();
-        
+
         if (e.getRepeatCount() < regex.repeatRangeHi[mem]) {
             if (e.getRepeatCount() >= regex.repeatRangeLo[mem]) {
                 int pcode = e.getRepeatPCode();
@@ -1554,43 +1634,43 @@ class ByteCodeMachine extends StackMachine {
             pushRepeatInc(si);
         }
     }
-    
+
     private void opRepeatIncNG() {
         int mem = code[ip++];
         int si = repeatStk[mem];
         repeatIncNG(mem, si);
     }
-    
+
     private void opRepeatIncNGSG() {
         int mem = code[ip++];
         int si = getRepeat(mem);
         repeatIncNG(mem, si);
     }
-    
+
     private void opPushPos() {
         pushPos(s, sprev);
     }
-    
+
     private void opPopPos() {
         StackEntry e = stack[posEnd()];
         s    = e.getStatePStr();
-        sprev= e.getStatePStrPrev();  
+        sprev= e.getStatePStrPrev();
     }
-    
+
     private void opPushPosNot() {
         int addr = code[ip++];
         pushPosNot(ip + addr, s, sprev);
     }
-    
+
     private void opFailPos() {
         popTilPosNot();
         opFail();
     }
-    
+
     private void opPushStopBT() {
         pushStopBT();
     }
-    
+
     private void opPopStopBT() {
         stopBtEnd();
     }
@@ -1606,9 +1686,9 @@ class ByteCodeMachine extends StackMachine {
         int tlen = code[ip++];
         s -= tlen;
         if (s < str) {opFail(); return;}
-        sprev = s == str ? -1 : s - 1; 
+        sprev = s == str ? -1 : s - 1;
     }
-    
+
     private void opPushLookBehindNot() {
         int addr = code[ip++];
         int tlen = code[ip++];
@@ -1629,25 +1709,25 @@ class ByteCodeMachine extends StackMachine {
         popTilLookBehindNot();
         opFail();
     }
-    
+
     private void opCall() {
         int addr = code[ip++];
         pushCallFrame(ip);
         ip = addr; // absolute address
     }
-    
+
     private void opReturn() {
         ip = sreturn();
         pushReturn();
     }
-    
+
     private void opFail() {
         if (stack == null) {
             ip = regex.codeLength - 1;
             return;
         }
 
-        
+
         StackEntry e = pop();
         ip    = e.getStatePCode();
         s     = e.getStatePStr();
@@ -1660,7 +1740,7 @@ class ByteCodeMachine extends StackMachine {
             }
         }
     }
-    
+
     private int finish() {
         return bestLen;
     }
diff --git a/src/org/joni/ByteCodePrinter.java b/src/org/joni/ByteCodePrinter.java
index 5efb353..77938da 100644
--- a/src/org/joni/ByteCodePrinter.java
+++ b/src/org/joni/ByteCodePrinter.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -27,42 +27,57 @@ import org.joni.constants.OPSize;
 import org.joni.exception.InternalException;
 
 class ByteCodePrinter {
-    int[]code;
-    int codeLength;
-    
+    final int[]code;
+    final int codeLength;
+    final byte[][] templates;
+
     Object[]operands;
     int operantCount;
     Encoding enc;
     WarnCallback warnings;
-    
+
     public ByteCodePrinter(Regex regex) {
         code = regex.code;
         codeLength = regex.codeLength;
         operands = regex.operands;
         operantCount = regex.operandLength;
-        enc = regex.enc;        
+
+        templates = regex.templates;
+        enc = regex.enc;
         warnings = regex.warnings;
     }
-    
+
     public String byteCodeListToString() {
         return compiledByteCodeListToString();
     }
-    
+
     private void pString(StringBuilder sb, int len, int s) {
         sb.append(":");
         while (len-- > 0) sb.append(new String(new byte[]{(byte)code[s++]}));
     }
-    
+
+    private void pStringFromTemplate(StringBuilder sb, int len, byte[]tm, int idx) {
+        sb.append(":T:");
+        while (len-- > 0) sb.append(new String(new byte[]{tm[idx++]}));
+    }
+
     private void pLenString(StringBuilder sb, int len, int mbLen, int s) {
         int x = len * mbLen;
         sb.append(":" + len + ":");
         while (x-- > 0) sb.append(new String(new byte[]{(byte)code[s++]}));
     }
-    
+
+    private void pLenStringFromTemplate(StringBuilder sb, int len, int mbLen, byte[]tm, int idx) {
+        int x = len * mbLen;
+        sb.append(":T:" + len + ":");
+        while (x-- > 0) sb.append(new String(new byte[]{(byte)tm[idx++]}));
+    }
+
     public int compiledByteCodeToString(StringBuilder sb, int bp) {
         int len, n, mem, addr, scn, cod;
         BitSet bs;
         CClassNode cc;
+        int tm, idx;
 
         sb.append("[" + OPCode.OpCodeNames[code[bp]]);
         int argType = OPCode.OpCodeArgTypes[code[bp]];
@@ -77,27 +92,27 @@ class ByteCodePrinter {
                 sb.append(":(" + code[bp] + ")");
                 bp += OPSize.RELADDR;
                 break;
-                
+
             case Arguments.ABSADDR:
                 sb.append(":(" + code[bp] + ")");
                 bp += OPSize.ABSADDR;
                 break;
-                
+
             case Arguments.LENGTH:
                 sb.append(":" + code[bp]);
                 bp += OPSize.LENGTH;
                 break;
-                
+
             case Arguments.MEMNUM:
                 sb.append(":" + code[bp]);
                 bp += OPSize.MEMNUM;
                 break;
-            
+
             case Arguments.OPTION:
                 sb.append(":" + code[bp]);
                 bp += OPSize.OPTION;
                 break;
-                
+
             case Arguments.STATE_CHECK:
                 sb.append(":" + code[bp]);
                 bp += OPSize.STATE_CHECK;
@@ -108,95 +123,140 @@ class ByteCodePrinter {
             case OPCode.EXACT1:
             case OPCode.ANYCHAR_STAR_PEEK_NEXT:
             case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT:
-            case OPCode.ANYCHAR_STAR_PEEK_NEXT_SB:            	
+            case OPCode.ANYCHAR_STAR_PEEK_NEXT_SB:
             case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB:
             	pString(sb, 1, bp++);
                 break;
-                
+
             case OPCode.EXACT2:
                 pString(sb, 2, bp);
                 bp += 2;
                 break;
-            
+
             case OPCode.EXACT3:
                 pString(sb, 3, bp);
                 bp += 3;
                 break;
-                
+
             case OPCode.EXACT4:
                 pString(sb, 4, bp);
                 bp += 4;
                 break;
-                
+
             case OPCode.EXACT5:
-                pString(sb, 5, bp);                
+                pString(sb, 5, bp);
                 bp += 5;
                 break;
-                
+
             case OPCode.EXACTN:
                 len = code[bp];
                 bp += OPSize.LENGTH;
-                pLenString(sb, len, 1, bp);
-                bp += len;
+                if (Config.USE_STRING_TEMPLATES) {
+                    tm = code[bp];
+                    bp += OPSize.INDEX;
+                    idx = code[bp];
+                    bp += OPSize.INDEX;
+                    pLenStringFromTemplate(sb, len, 1, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 1, bp);
+                    bp += len;
+                }
                 break;
-                
+
             case OPCode.EXACTMB2N1:
                 pString(sb, 2, bp);
                 bp += 2;
                 break;
-                
+
             case OPCode.EXACTMB2N2:
                 pString(sb, 4, bp);
                 bp += 4;
                 break;
-                
+
             case OPCode.EXACTMB2N3:
                 pString(sb, 6, bp);
                 bp += 6;
                 break;
-                
+
             case OPCode.EXACTMB2N:
                 len = code[bp];
                 bp += OPSize.LENGTH;
-                pLenString(sb, len, 2, bp);
-                bp += len * 2;
+                if (Config.USE_STRING_TEMPLATES) {
+                    tm = code[bp];
+                    bp += OPSize.INDEX;
+                    idx = code[bp];
+                    bp += OPSize.INDEX;
+                    pLenStringFromTemplate(sb, len, 2, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 2, bp);
+                    bp += len * 2;
+                }
                 break;
 
             case OPCode.EXACTMB3N:
                 len = code[bp];
                 bp += OPSize.LENGTH;
-                pLenString(sb, len, 3, bp);
-                bp += len * 3;
+                if (Config.USE_STRING_TEMPLATES) {
+                    tm = code[bp];
+                    bp += OPSize.INDEX;
+                    idx = code[bp];
+                    bp += OPSize.INDEX;
+                    pLenStringFromTemplate(sb, len, 3, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 3, bp);
+                    bp += len * 3;
+                }
                 break;
-                
+
             case OPCode.EXACTMBN:
                 int mbLen = code[bp];
                 bp += OPSize.LENGTH;
                 len = code[bp];
                 bp += OPSize.LENGTH;
-                sb.append(":" + mbLen + ":" + len + ":");
                 n = len * mbLen;
-                while (n-- > 0) sb.append(new String(new byte[]{(byte)code[bp++]}));
+
+                if (Config.USE_STRING_TEMPLATES) {
+                    tm = code[bp];
+                    bp += OPSize.INDEX;
+                    idx = code[bp];
+                    bp += OPSize.INDEX;
+                    sb.append(":T:" + mbLen + ":" + len + ":");
+
+                    while (n-- > 0) sb.append(new String(new byte[]{templates[tm][idx++]}));
+                } else {
+                    sb.append(":" + mbLen + ":" + len + ":");
+
+                    while (n-- > 0) sb.append(new String(new byte[]{(byte)code[bp++]}));
+                }
+
                 break;
-                
+
             case OPCode.EXACT1_IC:
             case OPCode.EXACT1_IC_SB:
                 final int MAX_CHAR_LENGTH = 6;
                 byte[]bytes = new byte[MAX_CHAR_LENGTH];
-                for (int i = 0; bp + i < code.length && i < MAX_CHAR_LENGTH; i++) bytes[i] = (byte)code[bp + i]; 
+                for (int i = 0; bp + i < code.length && i < MAX_CHAR_LENGTH; i++) bytes[i] = (byte)code[bp + i];
                 len = enc.length(bytes, 0, MAX_CHAR_LENGTH);
                 pString(sb, len, bp);
                 bp += len;
                 break;
-                
+
             case OPCode.EXACTN_IC:
             case OPCode.EXACTN_IC_SB:
                 len = code[bp];
                 bp += OPSize.LENGTH;
-                pLenString(sb, len, 1, bp);
-                bp += len;
+                if (Config.USE_STRING_TEMPLATES) {
+                    tm = code[bp];
+                    bp += OPSize.INDEX;
+                    idx = code[bp];
+                    bp += OPSize.INDEX;
+                    pLenStringFromTemplate(sb, len, 1, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 1, bp);
+                    bp += len;
+                }
                 break;
-                
+
             case OPCode.CCLASS:
             case OPCode.CCLASS_SB:
                 bs = new BitSet();
@@ -205,7 +265,7 @@ class ByteCodePrinter {
                 bp += BitSet.BITSET_SIZE;
                 sb.append(":" + n);
                 break;
-                
+
             case OPCode.CCLASS_NOT:
             case OPCode.CCLASS_NOT_SB:
                 bs = new BitSet();
@@ -214,15 +274,15 @@ class ByteCodePrinter {
                 bp += BitSet.BITSET_SIZE;
                 sb.append(":" + n);
                 break;
-                
+
             case OPCode.CCLASS_MB:
-            case OPCode.CCLASS_MB_NOT:                
+            case OPCode.CCLASS_MB_NOT:
                 len = code[bp];
                 bp += OPSize.LENGTH;
                 cod = code[bp];
                 //bp += OPSize.CODE_POINT;
                 bp += len;
-                sb.append(":" + cod + ":" + len); 
+                sb.append(":" + cod + ":" + len);
                 break;
 
             case OPCode.CCLASS_MIX:
@@ -245,13 +305,13 @@ class ByteCodePrinter {
                 n = cc.bs.numOn();
                 sb.append(":" + cc + ":" + n);
                 break;
-                
+
             case OPCode.BACKREFN_IC:
                 mem = code[bp];
                 bp += OPSize.MEMNUM;
                 sb.append(":" + mem);
                 break;
-                
+
             case OPCode.BACKREF_MULTI_IC:
             case OPCode.BACKREF_MULTI:
                 sb.append(" ");
@@ -264,7 +324,7 @@ class ByteCodePrinter {
                     sb.append(mem);
                 }
                 break;
-                
+
             case OPCode.BACKREF_WITH_LEVEL: {
                 int option = code[bp];
                 bp += OPSize.OPTION;
@@ -283,7 +343,7 @@ class ByteCodePrinter {
                 }
                 break;
             }
-            
+
             case OPCode.REPEAT:
             case OPCode.REPEAT_NG:
                 mem = code[bp];
@@ -292,7 +352,7 @@ class ByteCodePrinter {
                 bp += OPSize.RELADDR;
                 sb.append(":" + mem + ":" + addr);
                 break;
-                
+
             case OPCode.PUSH_OR_JUMP_EXACT1:
             case OPCode.PUSH_IF_PEEK_NEXT:
                 addr = code[bp];
@@ -301,14 +361,14 @@ class ByteCodePrinter {
                 pString(sb, 1, bp);
                 bp++;
                 break;
-                
+
             case OPCode.LOOK_BEHIND:
             case OPCode.LOOK_BEHIND_SB:
                 len = code[bp];
                 bp += OPSize.LENGTH;
                 sb.append(":" + len);
                 break;
-                
+
             case OPCode.PUSH_LOOK_BEHIND_NOT:
                 addr = code[bp];
                 bp += OPSize.RELADDR;
@@ -316,7 +376,7 @@ class ByteCodePrinter {
                 bp += OPSize.LENGTH;
                 sb.append(":" + len + ":(" + addr + ")");
                 break;
-                
+
             case OPCode.STATE_CHECK_PUSH:
             case OPCode.STATE_CHECK_PUSH_OR_JUMP:
                 scn = code[bp];
@@ -325,7 +385,7 @@ class ByteCodePrinter {
                 bp += OPSize.RELADDR;
                 sb.append(":" + scn + ":(" + addr + ")");
                 break;
-                
+
             default:
                 throw new InternalException("undefined code: " + code[--bp]);
             }
@@ -342,14 +402,14 @@ class ByteCodePrinter {
     private String compiledByteCodeListToString() {
         StringBuilder sb = new StringBuilder();
         sb.append("code length: " + codeLength + "\n");
-        
+
         int ncode = 0;
         int bp = 0;
         int end = codeLength;
-        
+
         while (bp < end) {
             ncode++;
-            
+
             if (bp > 0) sb.append(ncode % 5 == 0 ? "\n" : " ");
 
             bp = compiledByteCodeToString(sb, bp);
diff --git a/src/org/joni/CaptureTreeNode.java b/src/org/joni/CaptureTreeNode.java
index dd6549c..4cbd31d 100644
--- a/src/org/joni/CaptureTreeNode.java
+++ b/src/org/joni/CaptureTreeNode.java
@@ -1,27 +1,27 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
 
 public class CaptureTreeNode {
 
-    
+
     int group;
     int beg;
     int end;
@@ -34,7 +34,7 @@ public class CaptureTreeNode {
         end = Region.REGION_NOTPOS;
         group = -1;
     }
-    
+
     static final int HISTORY_TREE_INIT_ALLOC_SIZE = 8;
     void addChild(CaptureTreeNode child) {
         if (children == null) {
@@ -44,11 +44,11 @@ public class CaptureTreeNode {
             System.arraycopy(children, 0, tmp, 0, children.length);
             children = tmp;
         }
-        
+
         children[numChildren] = child;
         numChildren++;
     }
-    
+
     void clear() {
         for (int i=0; i<numChildren; i++) {
             children[i] = null; // ???
@@ -57,7 +57,7 @@ public class CaptureTreeNode {
         beg = end = Region.REGION_NOTPOS;
         group = -1;
     }
-    
+
     CaptureTreeNode cloneTree() {
         CaptureTreeNode clone = new CaptureTreeNode();
         clone.beg = beg;
@@ -69,6 +69,6 @@ public class CaptureTreeNode {
         }
         return clone;
     }
-    
-    
+
+
 }
diff --git a/src/org/joni/CodeRangeBuffer.java b/src/org/joni/CodeRangeBuffer.java
index 51b77e7..7e7e5e3 100644
--- a/src/org/joni/CodeRangeBuffer.java
+++ b/src/org/joni/CodeRangeBuffer.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -24,51 +24,51 @@ import org.joni.exception.ErrorMessages;
 import org.joni.exception.ValueException;
 
 public final class CodeRangeBuffer {
-    private static final int INIT_MULTI_BYTE_RANGE_SIZE = 5;    
+    private static final int INIT_MULTI_BYTE_RANGE_SIZE = 5;
     private static final int ALL_MULTI_BYTE_RANGE = 0x7fffffff;
-    
-    int[]p; 
+
+    int[]p;
     int used;
-    
+
     public CodeRangeBuffer(int[]ranges) {
         p = ranges;
         used = ranges[0] + 1;
     }
-    
+
     public CodeRangeBuffer() {
         p = new int[INIT_MULTI_BYTE_RANGE_SIZE];
         writeCodePoint(0, 0);
     }
-    
+
     public int[]getCodeRange() {
         return p;
     }
-    
+
     private CodeRangeBuffer(CodeRangeBuffer orig) {
         p = new int[orig.p.length];
         System.arraycopy(orig.p, 0, p, 0, p.length);
         used = orig.used;
     }
-    
+
     public String toString() {
         StringBuilder buf = new StringBuilder();
         buf.append("CodeRange");
         buf.append("\n  used: " + used);
         buf.append("\n  code point: " + p[0]);
         buf.append("\n  ranges: ");
-        
+
         for (int i=0; i<p[0]; i++) {
             buf.append("[" + rangeNumToString(p[i * 2 + 1]) + ".." + rangeNumToString(p[i * 2 + 2]) + "]");
             if (i > 0 && i % 6 == 0) buf.append("\n          ");
         }
-        
+
         return buf.toString();
     }
-    
+
     private static String rangeNumToString(int num){
         return "0x" + Integer.toString(num, 16);
     }
-    
+
     public void expand(int low) {
         int length = p.length;
         do { length <<= 1; } while (length < low);
@@ -76,27 +76,27 @@ public final class CodeRangeBuffer {
         System.arraycopy(p, 0, tmp, 0, used);
         p = tmp;
     }
-    
+
     public void ensureSize(int size) {
         int length = p.length;
         while (length < size ) { length <<= 1; }
-        if (p.length != length) { 
+        if (p.length != length) {
             int[]tmp = new int[length];
             System.arraycopy(p, 0, tmp, 0, used);
-            p = tmp;            
+            p = tmp;
         }
-    }   
-    
+    }
+
     private void moveRight(int from, int to, int n) {
         if (to + n > p.length) expand(to + n);
         System.arraycopy(p, from, p, to, n);
         if (to + n > used) used = to + n;
     }
-    
+
     protected void moveLeft(int from, int to, int n) {
         System.arraycopy(p, from, p, to, n);
     }
-    
+
     private void moveLeftAndReduce(int from, int to) {
         System.arraycopy(p, from, p, to, used - from);
         used -= from - to;
@@ -108,11 +108,11 @@ public final class CodeRangeBuffer {
         p[pos] = b;
         if (used < u) used = u;
     }
-    
+
     public CodeRangeBuffer clone() {
         return new CodeRangeBuffer(this);
     }
-    
+
     // ugly part: these methods should be made OO
     // add_code_range_to_buf
     public static CodeRangeBuffer addCodeRangeToBuff(CodeRangeBuffer pbuf, int from, int to) {
@@ -121,15 +121,15 @@ public final class CodeRangeBuffer {
             from = to;
             to = n;
         }
-        
+
         if (pbuf == null) pbuf = new CodeRangeBuffer(); // move to CClassNode
-        
+
         int[]p = pbuf.p;
         int n = p[0];
-        
+
         int low = 0;
         int bound = n;
-        
+
         while (low < bound) {
             int x = (low + bound) >>> 1;
             if (from > p[x * 2 + 2]) {
@@ -138,10 +138,10 @@ public final class CodeRangeBuffer {
                 bound = x;
             }
         }
-        
+
         int high = low;
         bound = n;
-        
+
         while (high < bound) {
             int x = (high + bound) >>> 1;
             if (to >= p[x * 2 + 1] - 1) {
@@ -150,38 +150,38 @@ public final class CodeRangeBuffer {
                 bound = x;
             }
         }
-        
+
         int incN = low + 1 - high;
 
-        if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);        
-        
+        if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);
+
         if (incN != 1) {
             if (from > p[low * 2 + 1]) from = p[low * 2 + 1];
             if (to < p[(high - 1) * 2 + 2]) to = p[(high - 1) * 2 + 2];
         }
-        
+
         if (incN != 0 && high < n) {
             int fromPos = 1 + high * 2;
             int toPos = 1 + (low + 1) * 2;
             int size = (n - high) * 2;
-            
+
             if (incN > 0) {
                 pbuf.moveRight(fromPos, toPos, size);
             } else {
                 pbuf.moveLeftAndReduce(fromPos, toPos);
             }
         }
-        
+
         int pos = 1 + low * 2;
         // pbuf.ensureSize(pos + 2);
         pbuf.writeCodePoint(pos, from);
         pbuf.writeCodePoint(pos + 1, to);
         n += incN;
         pbuf.writeCodePoint(0, n);
-        
+
         return pbuf;
     }
-    
+
     // add_code_range, be aware of it returning null!
     public static CodeRangeBuffer addCodeRange(CodeRangeBuffer pbuf, ScanEnvironment env, int from, int to) {
         if (from >to) {
@@ -198,26 +198,26 @@ public final class CodeRangeBuffer {
     protected static CodeRangeBuffer setAllMultiByteRange(Encoding enc, CodeRangeBuffer pbuf) {
         return addCodeRangeToBuff(pbuf, enc.mbcodeStartPosition(), ALL_MULTI_BYTE_RANGE);
     }
-    
+
     // ADD_ALL_MULTI_BYTE_RANGE
     public static CodeRangeBuffer addAllMultiByteRange(Encoding enc, CodeRangeBuffer pbuf) {
         if (!enc.isSingleByte()) return setAllMultiByteRange(enc, pbuf);
         return pbuf;
     }
-    
+
     // not_code_range_buf
     public static CodeRangeBuffer notCodeRangeBuff(Encoding enc, CodeRangeBuffer bbuf) {
         CodeRangeBuffer pbuf = null;
 
         if (bbuf == null) return setAllMultiByteRange(enc, pbuf);
-        
+
         int[]p = bbuf.p;
         int n = p[0];
-        
+
         if (n <= 0) return setAllMultiByteRange(enc, pbuf);
-        
+
         int pre = enc.mbcodeStartPosition();
-        
+
         int from;
         int to = 0;
         for (int i=0; i<n; i++) {
@@ -229,16 +229,16 @@ public final class CodeRangeBuffer {
             if (to == ALL_MULTI_BYTE_RANGE) break;
             pre = to + 1;
         }
-        
+
         if (to < ALL_MULTI_BYTE_RANGE) pbuf = addCodeRangeToBuff(pbuf, to + 1, ALL_MULTI_BYTE_RANGE);
         return pbuf;
     }
-    
+
     // or_code_range_buf
     public static CodeRangeBuffer orCodeRangeBuff(Encoding enc, CodeRangeBuffer bbuf1, boolean not1,
                                                                 CodeRangeBuffer bbuf2, boolean not2) {
         CodeRangeBuffer pbuf = null;
-        
+
         if (bbuf1 == null && bbuf2 == null) {
             if (not1 || not2) {
                 return setAllMultiByteRange(enc, pbuf);
@@ -251,13 +251,13 @@ public final class CodeRangeBuffer {
             boolean tnot;
             // swap
             tnot = not1; not1 = not2; not2 = tnot;
-            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf; 
+            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;
         }
-        
+
         if (bbuf1 == null) {
             if (not1) {
                 return setAllMultiByteRange(enc, pbuf);
-            } else { 
+            } else {
                 if (!not2) {
                     return bbuf2.clone();
                 } else {
@@ -265,13 +265,13 @@ public final class CodeRangeBuffer {
                 }
             }
         }
-        
+
         if (not1) {
             CodeRangeBuffer tbuf;
             boolean tnot;
             // swap
             tnot = not1; not1 = not2; not2 = tnot;
-            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf; 
+            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;
         }
 
         if (!not2 && !not1) { /* 1 OR 2 */
@@ -279,19 +279,19 @@ public final class CodeRangeBuffer {
         } else if (!not1) { /* 1 OR (not 2) */
             pbuf = notCodeRangeBuff(enc, bbuf2);
         }
-        
+
         int[]p1 = bbuf1.p;
         int n1 = p1[0];
-        
+
         for (int i=0; i<n1; i++) {
             int from = p1[i * 2 + 1];
             int to = p1[i * 2 + 2];
             pbuf = addCodeRangeToBuff(pbuf, from, to);
         }
-        
+
         return pbuf;
     }
-    
+
     // and_code_range1
     public static CodeRangeBuffer andCodeRange1(CodeRangeBuffer pbuf, int from1, int to1, int[]data, int n) {
         for (int i=0; i<n; i++) {
@@ -317,35 +317,35 @@ public final class CodeRangeBuffer {
             }
             if (from1 > to1) break;
         }
-        
+
         if (from1 <= to1) {
             pbuf = addCodeRangeToBuff(pbuf, from1, to1);
         }
-        
+
         return pbuf;
     }
-    
+
     // and_code_range_buf
     public static CodeRangeBuffer andCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
                                                    CodeRangeBuffer bbuf2, boolean not2) {
         CodeRangeBuffer pbuf = null;
-        
-        if (bbuf1 == null) { 
+
+        if (bbuf1 == null) {
             if (not1 && bbuf2 != null) return bbuf2.clone(); /* not1 != 0 -> not2 == 0 */
-            return null; 
+            return null;
         } else if (bbuf2 == null) {
             if (not2) return bbuf1.clone();
             return null;
         }
-        
+
         if (not1) {
             CodeRangeBuffer tbuf;
             boolean tnot;
             // swap
             tnot = not1; not1 = not2; not2 = tnot;
-            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;             
+            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;
         }
-        
+
         int[]p1 = bbuf1.p;
         int n1 = p1[0];
         int[]p2 = bbuf2.p;
diff --git a/src/org/joni/Compiler.java b/src/org/joni/Compiler.java
index c9ea261..00059f1 100644
--- a/src/org/joni/Compiler.java
+++ b/src/org/joni/Compiler.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -39,26 +39,26 @@ abstract class Compiler implements ErrorMessages {
     protected final Analyser analyser;
     protected final Encoding enc;
     protected final Regex regex;
-    
+
     protected Compiler(Analyser analyser) {
         this.analyser = analyser;
         this.regex = analyser.regex;
         this.enc = regex.enc;
     }
-    
+
     final void compile() {
         prepare();
         compileTree(analyser.root);
         finish();
     }
-    
+
     protected abstract void prepare();
     protected abstract void finish();
-    
+
     protected abstract void compileAltNode(ConsAltNode node);
-    
+
     private void compileStringRawNode(StringNode sn) {
-        if (sn.length() <= 0) return;        
+        if (sn.length() <= 0) return;
         addCompileString(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
     }
 
@@ -75,8 +75,8 @@ abstract class Compiler implements ErrorMessages {
         int prevLen = enc.length(bytes, p, end);
         p += prevLen;
         int slen = 1;
-        
-        while (p < end) {            
+
+        while (p < end) {
             int len = enc.length(bytes, p, end);
             if (len == prevLen) {
                 slen++;
@@ -88,11 +88,11 @@ abstract class Compiler implements ErrorMessages {
             }
             p += len;
         }
-        addCompileString(bytes, prev, prevLen, slen, ambig);        
+        addCompileString(bytes, prev, prevLen, slen, ambig);
     }
-    
+
     protected abstract void addCompileString(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase);
-    
+
     protected abstract void compileCClassNode(CClassNode node);
     protected abstract void compileCTypeNode(CTypeNode node);
     protected abstract void compileAnyCharNode();
@@ -103,7 +103,7 @@ abstract class Compiler implements ErrorMessages {
     protected abstract void compileOptionNode(EncloseNode node);
     protected abstract void compileEncloseNode(EncloseNode node);
     protected abstract void compileAnchorNode(AnchorNode node);
-    
+
     protected final void compileTree(Node node) {
         switch (node.getType()) {
         case NodeType.LIST:
@@ -112,11 +112,11 @@ abstract class Compiler implements ErrorMessages {
                 compileTree(lin.car);
             } while ((lin = lin.cdr) != null);
             break;
-            
+
         case NodeType.ALT:
             compileAltNode((ConsAltNode)node);
             break;
-            
+
         case NodeType.STR:
             StringNode sn = (StringNode)node;
             if (sn.isRaw()) {
@@ -125,15 +125,15 @@ abstract class Compiler implements ErrorMessages {
                 compileStringNode(sn);
             }
             break;
-            
+
         case NodeType.CCLASS:
             compileCClassNode((CClassNode)node);
             break;
-            
+
         case NodeType.CTYPE:
             compileCTypeNode((CTypeNode)node);
             break;
-            
+
         case NodeType.CANY:
             compileAnyCharNode();
             break;
@@ -141,14 +141,14 @@ abstract class Compiler implements ErrorMessages {
         case NodeType.BREF:
             compileBackrefNode((BackRefNode)node);
             break;
-            
+
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
                 compileCallNode((CallNode)node);
                 break;
             } // USE_SUBEXP_CALL
             break;
-            
+
         case NodeType.QTFR:
             if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
                 compileCECQuantifierNode((QuantifierNode)node);
@@ -156,7 +156,7 @@ abstract class Compiler implements ErrorMessages {
                 compileNonCECQuantifierNode((QuantifierNode)node);
             }
             break;
-            
+
         case NodeType.ENCLOSE:
             EncloseNode enode = (EncloseNode)node;
             if (enode.isOption()) {
@@ -164,7 +164,7 @@ abstract class Compiler implements ErrorMessages {
             } else {
                 compileEncloseNode(enode);
             }
-            break;            
+            break;
 
         case NodeType.ANCHOR:
             compileAnchorNode((AnchorNode)node);
@@ -175,7 +175,7 @@ abstract class Compiler implements ErrorMessages {
             newInternalException(ERR_PARSER_BUG);
         } // switch
     }
-    
+
     protected final void compileTreeNTimes(Node node, int n) {
         for (int i=0; i<n; i++) compileTree(node);
     }
diff --git a/src/org/joni/Config.java b/src/org/joni/Config.java
index 07762f0..3467966 100644
--- a/src/org/joni/Config.java
+++ b/src/org/joni/Config.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -23,25 +23,25 @@ import java.io.PrintStream;
 
 public interface Config extends org.jcodings.Config {
     final int CHAR_TABLE_SIZE = 256;
-    
+
     final boolean USE_NAMED_GROUP = true;
     final boolean USE_SUBEXP_CALL = true;
     final boolean USE_BACKREF_WITH_LEVEL = true;                            /* \k<name+n>, \k<name-n> */
-    
+
     final boolean USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT = true; /* /(?:()|())*\2/ */
     final boolean USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE = true;     /* /\n$/ =~ "\n" */
     final boolean USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR = false;
 
     final boolean CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS = true;
-    
+
     final boolean USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE = false;
     final boolean USE_CAPTURE_HISTORY = false;
     final boolean USE_VARIABLE_META_CHARS = true;
     final boolean USE_WORD_BEGIN_END = true;                                /* "\<": word-begin, "\>": word-end */
-    final boolean USE_POSIX_API_REGION_OPTION = true;                           /* needed for POSIX API support */ 
+    final boolean USE_POSIX_API_REGION_OPTION = true;                           /* needed for POSIX API support */
     final boolean USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE = true;
     final boolean USE_COMBINATION_EXPLOSION_CHECK = false;
-    
+
     final int NREGION                   = 10;
     final int MAX_BACKREF_NUM           = 1000;
     final int MAX_REPEAT_NUM            = 100000;
@@ -53,34 +53,40 @@ public interface Config extends org.jcodings.Config {
     // internal config
     final boolean USE_PARSE_TREE_NODE_RECYCLE       = true;
     final boolean USE_OP_PUSH_OR_JUMP_EXACT         = true;
-    final boolean USE_SHARED_CCLASS_TABLE			= false;    
-    final boolean USE_QTFR_PEEK_NEXT                = true; 
+    final boolean USE_SHARED_CCLASS_TABLE			= false;
+    final boolean USE_QTFR_PEEK_NEXT                = true;
 
     final int INIT_MATCH_STACK_SIZE                 = 64;
     final int DEFAULT_MATCH_STACK_LIMIT_SIZE        = 0;        /* unlimited */
     final int NUMBER_OF_POOLED_STACKS               = 4;
 
-    
-    
+
+
     final boolean DONT_OPTIMIZE                     = false;
-    
-    
+
+    final boolean USE_STRING_TEMPLATES              = true; // use embeded string templates in Regex object as byte arrays instead of compiling them into int bytecode array
+
+
     final int MAX_CAPTURE_HISTORY_GROUP             = 31;
-    
+
 
     final int CHECK_STRING_THRESHOLD_LEN            = 7;
     final int CHECK_BUFF_MAX_SIZE                   = 0x4000;
-    
-    
+
+    final boolean NON_UNICODE_SDW                   = true;
+
+
     final PrintStream log = System.out;
     final PrintStream err = System.err;
 
     final boolean DEBUG_ALL                         = false;
-    final boolean DEBUG                             = DEBUG_ALL;    
+
+    final boolean DEBUG                             = DEBUG_ALL;
     final boolean DEBUG_PARSE_TREE                  = DEBUG_ALL;
+    final boolean DEBUG_PARSE_TREE_RAW              = true;
     final boolean DEBUG_COMPILE                     = DEBUG_ALL;
     final boolean DEBUG_COMPILE_BYTE_CODE_INFO      = DEBUG_ALL;
-    final boolean DEBUG_SEARCH                      = DEBUG_ALL;    
+    final boolean DEBUG_SEARCH                      = DEBUG_ALL;
     final boolean DEBUG_MATCH                       = DEBUG_ALL;
     final boolean DEBUG_ASM                         = true;
     final boolean DEBUG_ASM_EXEC                    = true;
diff --git a/src/org/joni/Lexer.java b/src/org/joni/Lexer.java
index 172132f..bc919ad 100644
--- a/src/org/joni/Lexer.java
+++ b/src/org/joni/Lexer.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -22,6 +22,7 @@ package org.joni;
 import static org.joni.Option.isSingleline;
 import static org.joni.ast.QuantifierNode.isRepeatInfinite;
 
+import org.jcodings.Ptr;
 import org.jcodings.constants.CharacterType;
 import org.jcodings.exception.CharacterPropertyException;
 import org.joni.ast.QuantifierNode;
@@ -31,7 +32,7 @@ import org.joni.constants.TokenType;
 import org.joni.exception.ErrorMessages;
 
 class Lexer extends ScannerSupport {
-    protected final ScanEnvironment env; 
+    protected final ScanEnvironment env;
     protected final Syntax syntax;              // fast access to syntax
     protected final Token token = new Token();  // current token
 
@@ -40,17 +41,17 @@ class Lexer extends ScannerSupport {
         this.env = env;
         this.syntax = env.syntax;
     }
-    
+
     /**
      * @return 0: normal {n,m}, 2: fixed {n}
-     * !introduce returnCode here 
+     * !introduce returnCode here
      */
     private int fetchRangeQuantifier() {
         mark();
         boolean synAllow = syntax.allowInvalidInterval();
-        
+
         if (!left()) {
-            if (synAllow) { 
+            if (synAllow) {
                 return 1; /* "....{" : OK! */
             } else {
                 newSyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
@@ -63,7 +64,7 @@ class Lexer extends ScannerSupport {
                 newSyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
             }
         }
-        
+
         int low = scanUnsignedNumber();
         if (low < 0) newSyntaxException(ErrorMessages.ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
         if (low > Config.MAX_REPEAT_NUM) newSyntaxException(ErrorMessages.ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
@@ -77,18 +78,18 @@ class Lexer extends ScannerSupport {
                 return invalidRangeQuantifier(synAllow);
             }
         }
-        
+
         if (!left()) return invalidRangeQuantifier(synAllow);
-        
+
         fetch();
         int up;
         int ret = 0;
         if (c == ',') {
-            int prev = p; // ??? last            
+            int prev = p; // ??? last
             up = scanUnsignedNumber();
             if (up < 0) newValueException(ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
             if (up > Config.MAX_REPEAT_NUM) newValueException(ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
-            
+
             if (p == prev) {
                 if (nonLow) return invalidRangeQuantifier(synAllow);
                 up = QuantifierNode.REPEAT_INFINITE; /* {n,} : {n,infinite} */
@@ -99,28 +100,28 @@ class Lexer extends ScannerSupport {
             up = low; /* {n} : exact n times */
             ret = 2; /* fixed */
         }
-        
+
         if (!left()) return invalidRangeQuantifier(synAllow);
         fetch();
-        
+
         if (syntax.opEscBraceInterval()) {
             if (c != syntax.metaCharTable.esc) return invalidRangeQuantifier(synAllow);
             fetch();
         }
-        
+
         if (c != '}') return invalidRangeQuantifier(synAllow);
-        
+
         if (!isRepeatInfinite(up) && low > up) {
             newValueException(ERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE);
         }
-        
+
         token.type = TokenType.INTERVAL;
         token.setRepeatLower(low);
         token.setRepeatUpper(up);
-        
+
         return ret; /* 0: normal {n,m}, 2: fixed {n} */
     }
-    
+
     private int invalidRangeQuantifier(boolean synAllow) {
         if (synAllow) {
             restore();
@@ -130,7 +131,7 @@ class Lexer extends ScannerSupport {
             return 0; // not reached
         }
     }
-    
+
     /* \M-, \C-, \c, or \... */
     private int fetchEscapedValue() {
         if (!left()) newSyntaxException(ERR_END_PATTERN_AT_ESCAPE);
@@ -164,20 +165,20 @@ class Lexer extends ScannerSupport {
                 fetchEscapedValueBackSlash();
             }
             break;
-            
+
         case 'c':
             if (syntax.opEscCControl()) {
                 fetchEscapedValueControl();
             }
             /* fall through */
-            
+
         default:
             fetchEscapedValueBackSlash();
         } // switch
-        
+
         return c; // ???
     }
-    
+
     private void fetchEscapedValueBackSlash() {
         c = env.convertBackslashValue(c);
     }
@@ -194,7 +195,7 @@ class Lexer extends ScannerSupport {
             c &= 0x9f;
         }
     }
-    
+
     private int nameEndCodePoint(int start) {
         switch(start) {
         case '<':
@@ -212,16 +213,16 @@ class Lexer extends ScannerSupport {
         \k<num+n>,  \k<num-n>
         \k<-num+n>, \k<-num-n>
      */
-    
+
     // value implicit (rnameEnd)
-    private boolean fetchNameWithLevel(int startCode, int[]rbackNum, int[]rlevel) {
+    private boolean fetchNameWithLevel(int startCode, Ptr rbackNum, Ptr rlevel) {
         int src = p;
         boolean existLevel = false;
         int isNum = 0;
         int sign = 1;
-        
+
         int endCode = nameEndCodePoint(startCode);
-        int pnumHead = p; 
+        int pnumHead = p;
         int nameEnd = stop;
 
         String err = null;
@@ -232,15 +233,15 @@ class Lexer extends ScannerSupport {
             if (c == endCode) newValueException(ERR_EMPTY_GROUP_NAME);
             if (enc.isDigit(c)) {
                 isNum = 1;
-            } else if (c == '-') { 
+            } else if (c == '-') {
                 isNum = 2;
                 sign = -1;
                 pnumHead = p;
-            } else if (!enc.isWord(c)) { 
+            } else if (!enc.isWord(c)) {
                 err = ERR_INVALID_GROUP_NAME;
             }
         }
-        
+
         while (left()) {
             nameEnd = p;
             fetch();
@@ -248,7 +249,7 @@ class Lexer extends ScannerSupport {
                 if (isNum == 2) err = ERR_INVALID_GROUP_NAME;
                 break;
             }
-            
+
             if (isNum != 0) {
                 if (enc.isDigit(c)) {
                     isNum = 1;
@@ -271,13 +272,13 @@ class Lexer extends ScannerSupport {
                 unfetch();
                 int level = scanUnsignedNumber();
                 if (level < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                rlevel[0] = level * flag;
+                rlevel.p = level * flag;
                 existLevel = true;
-                
+
                 fetch();
                 isEndCode = c == endCode;
             }
-            
+
             if (!isEndCode) {
                 err = ERR_INVALID_GROUP_NAME;
                 nameEnd = stop;
@@ -295,7 +296,7 @@ class Lexer extends ScannerSupport {
                 } else if (backNum == 0) {
                     newValueException(ERR_INVALID_GROUP_NAME, src, stop);
                 }
-                rbackNum[0] = backNum * sign; 
+                rbackNum.p = backNum * sign;
             }
             value = nameEnd;
             return existLevel;
@@ -304,14 +305,14 @@ class Lexer extends ScannerSupport {
             return false; // not reached
         }
     }
-    
+
     // USE_NAMED_GROUP
     // ref: 0 -> define name    (don't allow number name)
     //      1 -> reference name (allow number name)
     private int fetchNameForNamedGroup(int startCode, boolean ref) {
         int src = p;
         value = 0;
-        
+
         int isNum = 0;
         int sign = 1;
 
@@ -332,7 +333,7 @@ class Lexer extends ScannerSupport {
                     err = ERR_INVALID_GROUP_NAME;
                     // isNum = 0;
                 }
-            } else if (c == '-') { 
+            } else if (c == '-') {
                 if (ref) {
                     isNum = 2;
                     sign = -1;
@@ -342,10 +343,10 @@ class Lexer extends ScannerSupport {
                     // isNum = 0;
                 }
             } else if (!enc.isWord(c)) {
-                err = ERR_INVALID_CHAR_IN_GROUP_NAME; 
+                err = ERR_INVALID_CHAR_IN_GROUP_NAME;
             }
         }
-        
+
         if (err == null) {
             while (left()) {
                 nameEnd = p;
@@ -354,7 +355,7 @@ class Lexer extends ScannerSupport {
                     if (isNum == 2) err = ERR_INVALID_GROUP_NAME;
                     break;
                 }
-                
+
                 if (isNum != 0) {
                     if (enc.isDigit(c)) {
                         isNum = 1;
@@ -372,7 +373,7 @@ class Lexer extends ScannerSupport {
                     }
                 }
             }
-            
+
             if (c != endCode) {
                 err = ERR_INVALID_GROUP_NAME;
                 nameEnd = stop;
@@ -410,12 +411,12 @@ class Lexer extends ScannerSupport {
     private final int fetchNameForNoNamedGroup(int startCode, boolean ref) {
         int src = p;
         value = 0;
-        
+
         int isNum = 0;
         int sign = 1;
-        
+
         int endCode = nameEndCodePoint(startCode);
-        int pnumHead = p; 
+        int pnumHead = p;
         int nameEnd = stop;
 
         String err = null;
@@ -424,7 +425,7 @@ class Lexer extends ScannerSupport {
         } else {
             fetch();
             if (c == endCode) newValueException(ERR_EMPTY_GROUP_NAME);
-            
+
             if (enc.isDigit(c)) {
                 isNum = 1;
             } else if (c == '-') {
@@ -438,17 +439,17 @@ class Lexer extends ScannerSupport {
 
         while(left()) {
             nameEnd = p;
-            
+
             fetch();
             if (c == endCode || c == ')') break;
             if (!enc.isDigit(c)) err = ERR_INVALID_CHAR_IN_GROUP_NAME;
         }
-            
-        if (err == null && c != endCode) { 
+
+        if (err == null && c != endCode) {
             err = ERR_INVALID_GROUP_NAME;
             nameEnd = stop;
         }
-        
+
         if (err == null) {
             mark();
             p = pnumHead;
@@ -460,7 +461,7 @@ class Lexer extends ScannerSupport {
                 newValueException(ERR_INVALID_GROUP_NAME, src, nameEnd);
             }
             backNum *= sign;
-            
+
             value = nameEnd;
             return backNum;
         } else {
@@ -468,7 +469,7 @@ class Lexer extends ScannerSupport {
             return 0; // not reached
         }
     }
-    
+
     protected final int fetchName(int startCode, boolean ref) {
         if (Config.USE_NAMED_GROUP) {
             return fetchNameForNamedGroup(startCode, ref);
@@ -476,14 +477,13 @@ class Lexer extends ScannerSupport {
             return fetchNameForNoNamedGroup(startCode, ref);
         }
     }
-    
+
     private boolean strExistCheckWithEsc(int[]s, int n, int bad) {
         int p = this.p;
         int to = this.stop;
-        
+
         boolean inEsc = false;
         int i=0;
-
         while(p < to) {
             if (inEsc) {
                 inEsc = false;
@@ -508,14 +508,133 @@ class Lexer extends ScannerSupport {
             }
         }
         return false;
-    }    
-    
-    private static final int send[] = new int[]{':', ']'}; 
-    
+    }
+
+    private static final int send[] = new int[]{':', ']'};
+
+    private void fetchTokenInCCFor_charType(boolean flag, int type) {
+        token.type = TokenType.CHAR_TYPE;
+        token.setPropCType(type);
+        token.setPropNot(flag);
+    }
+
+    private void fetchTokenInCCFor_p() {
+        int c2 = peek(); // !!! migrate to peekIs
+        if (c2 == '{' && syntax.op2EscPBraceCharProperty()) {
+            inc();
+            token.type = TokenType.CHAR_PROPERTY;
+            token.setPropNot(c == 'P');
+
+            if (syntax.op2EscPBraceCircumflexNot()) {
+                c2 = fetchTo();
+                if (c2 == '^') {
+                    token.setPropNot(!token.getPropNot());
+                } else {
+                    unfetch();
+                }
+            }
+        } else {
+            syntaxWarn(Warnings.INVALID_UNICODE_PROPERTY, (char)c);
+        }
+    }
+
+    private void fetchTokenInCCFor_x() {
+        if (!left()) return;
+        int last = p;
+
+        if (peekIs('{') && syntax.opEscXBraceHex8()) {
+            inc();
+            int num = scanUnsignedHexadecimalNumber(8);
+            if (num < 0) newValueException(ERR_TOO_BIG_WIDE_CHAR_VALUE);
+            if (left()) {
+                int c2 = peek();
+                if (enc.isXDigit(c2)) newValueException(ERR_TOO_LONG_WIDE_CHAR_VALUE);
+            }
+
+            if (p > last + enc.length(bytes, last, stop) && left() && peekIs('}')) {
+                inc();
+                token.type = TokenType.CODE_POINT;
+                token.base = 16;
+                token.setCode(num);
+            } else {
+                /* can't read nothing or invalid format */
+                p = last;
+            }
+        } else if (syntax.opEscXHex2()) {
+            int num = scanUnsignedHexadecimalNumber(2);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 16;
+            token.setC(num);
+        }
+    }
+
+    private void fetchTokenInCCFor_u() {
+        if (!left()) return;
+        int last = p;
+
+        if (syntax.op2EscUHex4()) {
+            int num = scanUnsignedHexadecimalNumber(4);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) {  /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.CODE_POINT;
+            token.base = 16;
+            token.setCode(num);
+        }
+    }
+
+    private void fetchTokenInCCFor_digit() {
+        if (syntax.opEscOctal3()) {
+            unfetch();
+            int last = p;
+            int num = scanUnsignedOctalNumber(3);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) {  /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 8;
+            token.setC(num);
+        }
+    }
+
+    private void fetchTokenInCCFor_posixBracket() {
+        if (syntax.opPosixBracket() && peekIs(':')) {
+            token.backP = p; /* point at '[' is readed */
+            inc();
+            if (strExistCheckWithEsc(send, send.length, ']')) {
+                token.type = TokenType.POSIX_BRACKET_OPEN;
+            } else {
+                unfetch();
+                // remove duplication, goto cc_in_cc;
+                if (syntax.op2CClassSetOp()) {
+                    token.type = TokenType.CC_CC_OPEN;
+                } else {
+                    env.ccEscWarn("[");
+                }
+            }
+        } else { // cc_in_cc:
+            if (syntax.op2CClassSetOp()) {
+                token.type = TokenType.CC_CC_OPEN;
+            } else {
+                env.ccEscWarn("[");
+            }
+        }
+    }
+
+    private void fetchTokenInCCFor_and() {
+        if (syntax.op2CClassSetOp() && left() && peekIs('&')) {
+            inc();
+            token.type = TokenType.CC_AND;
+        }
+    }
+
     protected final TokenType fetchTokenInCC() {
-        int last;
-        int c2;
-        
         if (!left()) {
             token.type = TokenType.EOT;
             return token.type;
@@ -526,7 +645,7 @@ class Lexer extends ScannerSupport {
         token.base = 0;
         token.setC(c);
         token.escaped = false;
-        
+
         if (c == ']') {
             token.type = TokenType.CC_CLOSE;
         } else if (c == '-') {
@@ -539,126 +658,40 @@ class Lexer extends ScannerSupport {
             token.setC(c);
 
             switch (c) {
-            
             case 'w':
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.WORD);
-                token.setPropNot(false);
+                fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
                 break;
-                
             case 'W':
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.WORD);
-                token.setPropNot(true);
+                fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
                 break;
-                
             case 'd':
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.DIGIT);
-                token.setPropNot(false);
+                fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
                 break;
-
             case 'D':
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.DIGIT);
-                token.setPropNot(true);
+                fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
                 break;
-
             case 's':
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.SPACE);
-                token.setPropNot(false);
+                fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
                 break;
-            
             case 'S':
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.SPACE);
-                token.setPropNot(true);
+                fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
                 break;
-
             case 'h':
-                if (!syntax.op2EscHXDigit()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.XDIGIT);
-                token.setPropNot(false);
+                if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
                 break;
-
             case 'H':
-                if (!syntax.op2EscHXDigit()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.XDIGIT);
-                token.setPropNot(true);
+                if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
                 break;
-            
             case 'p':
             case 'P':
-                c2 = peek(); // !!! migrate to peekIs 
-                if (c2 == '{' && syntax.op2EscPBraceCharProperty()) {
-                    inc();
-                    token.type = TokenType.CHAR_PROPERTY;
-                    token.setPropNot(c == 'P');
-                    
-                    if (syntax.op2EscPBraceCircumflexNot()) {
-                        c2 = fetchTo();
-                        if (c2 == '^') {
-                            token.setPropNot(!token.getPropNot()); 
-                        } else {
-                            unfetch();
-                        }
-                    }
-                }
+                fetchTokenInCCFor_p();
                 break;
-                
             case 'x':
-                if (!left()) break;
-                last = p;
-                
-                if (peekIs('{') && syntax.opEscXBraceHex8()) {
-                    inc();
-                    int num = scanUnsignedHexadecimalNumber(8);
-                    if (num < 0) newValueException(ERR_TOO_BIG_WIDE_CHAR_VALUE);
-                    if (left()) {
-                        c2 = peek();
-                        if (enc.isXDigit(c2)) newValueException(ERR_TOO_LONG_WIDE_CHAR_VALUE); 
-                    }
-                    
-                    if (p > last + enc.length(bytes, last, stop) && left() && peekIs('}')) {
-                        inc();                      
-                        token.type = TokenType.CODE_POINT;
-                        token.base = 16;
-                        token.setCode(num);
-                    } else {
-                        /* can't read nothing or invalid format */
-                        p = last;
-                    }
-                } else if (syntax.opEscXHex2()) {
-                    int num = scanUnsignedHexadecimalNumber(2);
-                    if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                    if (p == last) { /* can't read nothing. */
-                        num = 0; /* but, it's not error */
-                    }
-                    token.type = TokenType.RAW_BYTE;
-                    token.base = 16;
-                    token.setC(num);
-                }
+                fetchTokenInCCFor_x();
                 break;
-                
             case 'u':
-                if (!left()) break;
-                last = p;
-                
-                if (syntax.op2EscUHex4()) {
-                    int num = scanUnsignedHexadecimalNumber(4);
-                    if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                    if (p == last) {  /* can't read nothing. */
-                        num = 0; /* but, it's not error */
-                    }
-                    token.type = TokenType.CODE_POINT;
-                    token.base = 16;
-                    token.setCode(num);
-                }
+                fetchTokenInCCFor_u();
                 break;
-                
             case '0':
             case '1':
             case '2':
@@ -667,20 +700,9 @@ class Lexer extends ScannerSupport {
             case '5':
             case '6':
             case '7':
-                if (syntax.opEscOctal3()) {
-                    unfetch();
-                    last = p;
-                    int num = scanUnsignedOctalNumber(3);
-                    if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                    if (p == last) {  /* can't read nothing. */
-                        num = 0; /* but, it's not error */
-                    }
-                    token.type = TokenType.RAW_BYTE;
-                    token.base = 8;
-                    token.setC(num);
-                }
+                fetchTokenInCCFor_digit();
                 break;
-                
+
             default:
                 unfetch();
                 int num = fetchEscapedValue();
@@ -690,659 +712,513 @@ class Lexer extends ScannerSupport {
                 }
                 break;
             } // switch
-            
+
         } else if (c == '[') {
-            if (syntax.opPosixBracket() && peekIs(':')) {
-                token.backP = p; /* point at '[' is readed */
-                inc();
-                if (strExistCheckWithEsc(send, send.length, ']')) {
-                    token.type = TokenType.POSIX_BRACKET_OPEN;
-                } else {
-                    unfetch();
-                    // remove duplication, goto cc_in_cc;
-                    if (syntax.op2CClassSetOp()) {
-                        token.type = TokenType.CC_CC_OPEN;
-                    } else {
-                        env.ccEscWarn("[");
-                    }
-                }
-            } else { // cc_in_cc:
-                if (syntax.op2CClassSetOp()) {
-                    token.type = TokenType.CC_CC_OPEN;
-                } else {
-                    env.ccEscWarn("[");
-                }
-            }
+            fetchTokenInCCFor_posixBracket();
         } else if (c == '&') {
-            if (syntax.op2CClassSetOp() && left() && peekIs('&')) {
-                inc();
-                token.type = TokenType.CC_AND;
-            }
+            fetchTokenInCCFor_and();
         }
         return token.type;
     }
-    
+
     protected final int backrefRelToAbs(int relNo) {
         return env.numMem + 1 + relNo;
     }
-    
-    protected final TokenType fetchToken() {
-        int last;
-        
-        // mark(); // out
-        
-        start:
-        while(true) {
-            
-        if (!left()) {
-            token.type = TokenType.EOT;
-            return token.type;
-        }
-        
-        token.type = TokenType.STRING;
-        token.base = 0;
-        token.backP = p;
 
-        fetch();
-
-        if (c == syntax.metaCharTable.esc && !syntax.op2IneffectiveEscape()) { // IS_MC_ESC_CODE(code, syn)
-            if (!left()) newSyntaxException(ERR_END_PATTERN_AT_ESCAPE);
-
-            token.backP = p;
-            fetch();
-
-            token.setC(c);
-            token.escaped = true;
-            switch(c) {
-
-            case '*':
-                if (!syntax.opEscAsteriskZeroInf()) break;
-                token.type = TokenType.OP_REPEAT;
-                token.setRepeatLower(0);
-                token.setRepeatUpper(QuantifierNode.REPEAT_INFINITE);
-                greedyCheck();
-                break;
-
-            case '+':
-                if (!syntax.opEscPlusOneInf()) break;
-                token.type = TokenType.OP_REPEAT;
-                token.setRepeatLower(1);
-                token.setRepeatUpper(QuantifierNode.REPEAT_INFINITE);
-                greedyCheck();
-                break;
+    private void fetchTokenFor_repeat(int lower, int upper) {
+        token.type = TokenType.OP_REPEAT;
+        token.setRepeatLower(lower);
+        token.setRepeatUpper(upper);
+        greedyCheck();
+    }
 
-            case '?':
-                if (!syntax.opEscQMarkZeroOne()) break;
-                token.type = TokenType.OP_REPEAT;
-                token.setRepeatLower(0);
-                token.setRepeatUpper(1);
+    private void fetchTokenFor_openBrace() {
+        switch (fetchRangeQuantifier()) {
+        case 0:
+            greedyCheck();
+            break;
+        case 2:
+            if (syntax.fixedIntervalIsGreedyOnly()) {
+                possessiveCheck();
+            } else {
                 greedyCheck();
-                break;
-
-            case '{':
-                if (!syntax.opEscBraceInterval()) break;
-                switch (fetchRangeQuantifier()) {
-                case 0:
-                    greedyCheck();
-                    break;
-                case 2:
-                    if (syntax.fixedIntervalIsGreedyOnly()) {
-                        possessiveCheck();
-                    } else {
-                        greedyCheck();
-                    }
-                    break;
-                default: /* 1 : normal char */
-                } // inner switch
-                break;
-
-            case '|':
-                if (!syntax.opEscVBarAlt()) break;
-                token.type = TokenType.ALT;
-                break;
-
-            case '(':
-                if (!syntax.opEscLParenSubexp()) break;
-                token.type = TokenType.SUBEXP_OPEN;
-                break;
+            }
+            break;
+        default: /* 1 : normal char */
+        } // inner switch
+    }
 
-            case ')':
-                if (!syntax.opEscLParenSubexp()) break;
-                token.type = TokenType.SUBEXP_CLOSE;
-                break;
+    private void fetchTokenFor_anchor(int subType) {
+        token.type = TokenType.ANCHOR;
+        token.setAnchor(subType);
+    }
 
-            case 'w':
-                if (!syntax.opEscWWord()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.WORD);
-                token.setPropNot(false);
-                break;
+    private void fetchTokenFor_xBrace() {
+        if (!left()) return;
 
-            case 'W':
-                if (!syntax.opEscWWord()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.WORD);
-                token.setPropNot(true);
-                break;
+        int last = p;
+        if (peekIs('{') && syntax.opEscXBraceHex8()) {
+            inc();
+            int num = scanUnsignedHexadecimalNumber(8);
+            if (num < 0) newValueException(ERR_TOO_BIG_WIDE_CHAR_VALUE);
+            if (left()) {
+                if (enc.isXDigit(peek())) newValueException(ERR_TOO_LONG_WIDE_CHAR_VALUE);
+            }
 
-            case 'b':
-                if (!syntax.opEscBWordBound()) break;
-                token.type = TokenType.ANCHOR;
-                token.setAnchor(AnchorType.WORD_BOUND);
-                break;
+            if (p > last + enc.length(bytes, last, stop) && left() && peekIs('}')) {
+                inc();
+                token.type = TokenType.CODE_POINT;
+                token.setCode(num);
+            } else {
+                /* can't read nothing or invalid format */
+                p = last;
+            }
+        } else if (syntax.opEscXHex2()) {
+            int num = scanUnsignedHexadecimalNumber(2);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 16;
+            token.setC(num);
+        }
+    }
 
-            case 'B':
-                if (!syntax.opEscBWordBound()) break;
-                token.type = TokenType.ANCHOR;
-                token.setAnchor(AnchorType.NOT_WORD_BOUND);
-                break;
+    private void fetchTokenFor_uHex() {
+        if (!left()) return;
+        int last = p;
 
-            case '<':
-                if (Config.USE_WORD_BEGIN_END) {
-                    if (!syntax.opEscLtGtWordBeginEnd()) break;
-                    token.type = TokenType.ANCHOR;
-                    token.setAnchor(AnchorType.WORD_BEGIN);
-                    break;
-                } // USE_WORD_BEGIN_END
-                break; // ?
-                
-            case '>':
-                if (Config.USE_WORD_BEGIN_END) {
-                    if (!syntax.opEscLtGtWordBeginEnd()) break;
-                    token.type = TokenType.ANCHOR;
-                    token.setAnchor(AnchorType.WORD_END);
-                    break;
-                } // USE_WORD_BEGIN_END
-                break; // ?
+        if (syntax.op2EscUHex4()) {
+            int num = scanUnsignedHexadecimalNumber(4);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.CODE_POINT;
+            token.base = 16;
+            token.setCode(num);
+        }
+    }
 
-            case 's':
-                if (!syntax.opEscSWhiteSpace()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.SPACE);
-                token.setPropNot(false);
-                break;
+    private void fetchTokenFor_digit() {
+        unfetch();
+        int last = p;
+        int num = scanUnsignedNumber();
+        if (num < 0 || num > Config.MAX_BACKREF_NUM) { // goto skip_backref
+        } else if (syntax.opDecimalBackref() && (num <= env.numMem || num <= 9)) { /* This spec. from GNU regex */
+            if (syntax.strictCheckBackref()) {
+                if (num > env.numMem || env.memNodes == null || env.memNodes[num] == null) newValueException(ERR_INVALID_BACKREF);
+            }
+            token.type = TokenType.BACKREF;
+            token.setBackrefNum(1);
+            token.setBackrefRef1(num);
+            token.setBackrefByName(false);
+            if (Config.USE_BACKREF_WITH_LEVEL) token.setBackrefExistLevel(false);
+            return;
+        }
 
-            case 'S':
-                if (!syntax.opEscSWhiteSpace()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.SPACE);
-                token.setPropNot(true);
-                break;
-                
-            case 'd':
-                if (!syntax.opEscDDigit()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.DIGIT);
-                token.setPropNot(false);
-                break;
-                
-            case 'D':
-                if (!syntax.opEscDDigit()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.DIGIT);
-                token.setPropNot(true);
-                break;
+        if (c == '8' || c == '9') { /* normal char */ // skip_backref:
+            p = last;
+            inc();
+            return;
+        }
+        p = last;
 
-            case 'h':
-                if (!syntax.op2EscHXDigit()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.XDIGIT);
-                token.setPropNot(false);
-                break;
+        fetchTokenFor_zero(); /* fall through */
+    }
 
-            case 'H':
-                if (!syntax.op2EscHXDigit()) break;
-                token.type = TokenType.CHAR_TYPE;
-                token.setPropCType(CharacterType.XDIGIT);
-                token.setPropNot(true);
-                break;
+    private void fetchTokenFor_zero() {
+        if (syntax.opEscOctal3()) {
+            int last = p;
+            int num = scanUnsignedOctalNumber(c == '0' ? 2 : 3);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 8;
+            token.setC(num);
+        } else if (c != '0') {
+            inc();
+        }
+    }
 
-            case 'A':
-                if (!syntax.opEscAZBufAnchor()) break;
-                // begin_buf label
-                token.type = TokenType.ANCHOR;
-                token.setSubtype(AnchorType.BEGIN_BUF);
-                break;
-                
-            case 'Z':
-                if (!syntax.opEscAZBufAnchor()) break;
-                token.type = TokenType.ANCHOR;
-                token.setSubtype(AnchorType.SEMI_END_BUF);
-                break;
-                
-            case 'z':
-                if (!syntax.opEscAZBufAnchor()) break;
-                // end_buf label                
-                token.type = TokenType.ANCHOR;                
-                token.setSubtype(AnchorType.END_BUF);
-                break;
-                
-            case 'G':
-                if (!syntax.opEscCapitalGBeginAnchor()) break;
-                token.type = TokenType.ANCHOR;
-                token.setSubtype(AnchorType.BEGIN_POSITION);
-                break;
-                
-            case '`':
-                if (!syntax.op2EscGnuBufAnchor()) break;
-                // goto begin_buf
-                token.type = TokenType.ANCHOR;
-                token.setSubtype(AnchorType.BEGIN_BUF);
-                break;
+    private void fetchTokenFor_namedBackref() {
+        if (syntax.op2EscKNamedBackref()) {
+            if (left()) {
+                fetch();
+                if (c =='<' || c == '\'') {
+                    int last = p;
+                    int backNum;
+                    if (Config.USE_BACKREF_WITH_LEVEL) {
+                        Ptr rbackNum = new Ptr();
+                        Ptr rlevel = new Ptr();
+                        token.setBackrefExistLevel(fetchNameWithLevel(c, rbackNum, rlevel));
+                        token.setBackrefLevel(rlevel.p);
+                        backNum = rbackNum.p;
+                    } else {
+                        backNum = fetchName(c, true);
+                    } // USE_BACKREF_AT_LEVEL
+                    int nameEnd = value; // set by fetchNameWithLevel/fetchName
 
-            case '\'':
-                if (!syntax.op2EscGnuBufAnchor()) break;
-                // goto end_buf                
-                token.type = TokenType.ANCHOR;                
-                token.setSubtype(AnchorType.END_BUF);
-                break;
+                    if (backNum != 0) {
+                        if (backNum < 0) {
+                            backNum = backrefRelToAbs(backNum);
+                            if (backNum <= 0) newValueException(ERR_INVALID_BACKREF);
+                        }
 
-            case 'x': // extract to helper for all 'x'
-                if (!left()) break;
-                last = p;
-                if (peekIs('{') && syntax.opEscXBraceHex8()) {
-                    inc();
-                    int num = scanUnsignedHexadecimalNumber(8);
-                    if (num < 0) newValueException(ERR_TOO_BIG_WIDE_CHAR_VALUE);
-                    if (left()) {
-                        if (enc.isXDigit(peek())) newValueException(ERR_TOO_LONG_WIDE_CHAR_VALUE); 
-                    }
-                    
-                    if (p > last + enc.length(bytes, last, stop) && left() && peekIs('}')) {
-                        inc();
-                        token.type = TokenType.CODE_POINT;
-                        token.setCode(num);
+                        if (syntax.strictCheckBackref() && (backNum > env.numMem || env.memNodes == null)) {
+                            newValueException(ERR_INVALID_BACKREF);
+                        }
+                        token.type = TokenType.BACKREF;
+                        token.setBackrefByName(false);
+                        token.setBackrefNum(1);
+                        token.setBackrefRef1(backNum);
                     } else {
-                        /* can't read nothing or invalid format */
-                        p = last;
-                    }
-                } else if (syntax.opEscXHex2()) {
-                    int num = scanUnsignedHexadecimalNumber(2);
-                    if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                    if (p == last) { /* can't read nothing. */
-                        num = 0; /* but, it's not error */
-                    }
-                    token.type = TokenType.RAW_BYTE;
-                    token.base = 16;
-                    token.setC(num);
-                }
-                break;
-                
-            case 'u': // extract to helper
-                if (!left()) break;
-                last = p;
-                
-                if (syntax.op2EscUHex4()) {
-                    int num = scanUnsignedHexadecimalNumber(4);
-                    if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                    if (p == last) { /* can't read nothing. */
-                        num = 0; /* but, it's not error */
-                    }
-                    token.type = TokenType.CODE_POINT;
-                    token.base = 16;
-                    token.setCode(num);
-                }
-                break;
-                
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':               
-                unfetch();
-                last = p;
-                int num = scanUnsignedNumber();
-                if (num < 0 || num > Config.MAX_BACKREF_NUM) { 
-                    // goto skip_backref
-                } else if (syntax.opDecimalBackref() && (num <= env.numMem || num <= 9)) { /* This spec. from GNU regex */
-                    if (syntax.strictCheckBackref()) {
-                        if (num > env.numMem || env.memNodes == null || env.memNodes[num] == null) newValueException(ERR_INVALID_BACKREF);
-                    }
-                    token.type = TokenType.BACKREF;
-                    token.setBackrefNum(1);
-                    token.setBackrefRef1(num);
-                    token.setBackrefByName(false);
-                    if (Config.USE_BACKREF_WITH_LEVEL) token.setBackrefExistLevel(false);
-                    break;
-                }
-                // skip_backref:
-                if (c == '8' || c == '9') {
-                    /* normal char */
-                    p = last;
-                    inc();
-                    break;
-                }
-                p = last;
-                /* fall through */
-                
-            case '0':
-                if (syntax.opEscOctal3()) {
-                    last = p;
-                    num = scanUnsignedOctalNumber(c == '0' ? 2 : 3);
-                    if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
-                    if (p == last) { /* can't read nothing. */
-                        num = 0; /* but, it's not error */
-                    }
-                    token.type = TokenType.RAW_BYTE;
-                    token.base = 8;
-                    token.setC(num);
-                } else if (c != '0') {
-                    inc();
-                }
-                break;
-                
-            case 'k':
-                if (Config.USE_NAMED_GROUP) {
-                    if (syntax.op2EscKNamedBackref()) {
-                        fetch();
-                        if (c =='<' || c == '\'') {
-                            last = p;
-                            int backNum;
-                            if (Config.USE_BACKREF_WITH_LEVEL) {
-                                int[]rbackNum = new int[1];
-                                int[]rlevel = new int[1];
-                                token.setBackrefExistLevel(fetchNameWithLevel(c, rbackNum, rlevel));
-                                token.setBackrefLevel(rlevel[0]);
-                                backNum = rbackNum[0];
-                            } else {
-                                backNum = fetchName(c, true);
-                            } // USE_BACKREF_AT_LEVEL
-                            int nameEnd = value; // set by fetchNameWithLevel/fetchName
-                            
-                            if (backNum != 0) {
-                                if (backNum < 0) {
-                                    backNum = backrefRelToAbs(backNum);
-                                    if (backNum <= 0) newValueException(ERR_INVALID_BACKREF);
-                                }
-                                
-                                if (syntax.strictCheckBackref() && (backNum > env.numMem || env.memNodes == null)) {
-                                    newValueException(ERR_INVALID_BACKREF);
-                                }
-                                token.type = TokenType.BACKREF;
-                                token.setBackrefByName(false);
-                                token.setBackrefNum(1);
-                                token.setBackrefRef1(backNum);
-                            } else {
-                                NameEntry e = env.reg.nameToGroupNumbers(bytes, last, nameEnd);
-                                if (e == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, last, nameEnd);
-
-                                if (syntax.strictCheckBackref()) {
-                                    if (e.backNum == 1) {
-                                        if (e.backRef1 > env.numMem ||
-                                            env.memNodes == null ||
-                                            env.memNodes[e.backRef1] == null) newValueException(ERR_INVALID_BACKREF);
-                                    } else {
-                                        for (int i=0; i<e.backNum; i++) {
-                                            if (e.backRefs[i] > env.numMem ||
-                                                env.memNodes == null ||
-                                                env.memNodes[e.backRefs[i]] == null) newValueException(ERR_INVALID_BACKREF);
-                                        }
-                                    }
-                                }
+                        NameEntry e = env.reg.nameToGroupNumbers(bytes, last, nameEnd);
+                        if (e == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, last, nameEnd);
 
-                                token.type = TokenType.BACKREF;
-                                token.setBackrefByName(true);
-
-                                if (e.backNum == 1) {
-                                    token.setBackrefNum(1);
-                                    token.setBackrefRef1(e.backRef1);
-                                } else {
-                                    token.setBackrefNum(e.backNum);
-                                    token.setBackrefRefs(e.backRefs);
+                        if (syntax.strictCheckBackref()) {
+                            if (e.backNum == 1) {
+                                if (e.backRef1 > env.numMem ||
+                                    env.memNodes == null ||
+                                    env.memNodes[e.backRef1] == null) newValueException(ERR_INVALID_BACKREF);
+                            } else {
+                                for (int i=0; i<e.backNum; i++) {
+                                    if (e.backRefs[i] > env.numMem ||
+                                        env.memNodes == null ||
+                                        env.memNodes[e.backRefs[i]] == null) newValueException(ERR_INVALID_BACKREF);
                                 }
                             }
-                        } else {
-                            unfetch();
                         }
-                    }
-                    
-                    break;
-                } // USE_NAMED_GROUP
-                break;
-                
-            case 'g':
-                if (Config.USE_SUBEXP_CALL) {
-                    if (syntax.op2EscGSubexpCall()) {
-                        fetch();
-                        if (c == '<' || c == '\'') {
-                            last = p;
-                            int gNum = fetchName(c, true);
-                            int nameEnd = value;
-                            token.type = TokenType.CALL;
-                            token.setCallNameP(last);
-                            token.setCallNameEnd(nameEnd);
-                            token.setCallGNum(gNum);
+
+                        token.type = TokenType.BACKREF;
+                        token.setBackrefByName(true);
+
+                        if (e.backNum == 1) {
+                            token.setBackrefNum(1);
+                            token.setBackrefRef1(e.backRef1);
                         } else {
-                            unfetch();
+                            token.setBackrefNum(e.backNum);
+                            token.setBackrefRefs(e.backRefs);
                         }
                     }
-                    break;                    
-                } // USE_SUBEXP_CALL
-                break;
-                
-            case 'Q':
-                if (syntax.op2EscCapitalQQuote()) {
-                    token.type = TokenType.QUOTE_OPEN;
+                } else {
+                    unfetch();
+                    syntaxWarn(Warnings.INVALID_BACKREFERENCE);
                 }
-                break;
-                
-            case 'p':
-            case 'P':
-                if (peekIs('{') && syntax.op2EscPBraceCharProperty()) {
-                    inc();
-                    token.type = TokenType.CHAR_PROPERTY;
-                    token.setPropNot(c == 'P');
-                    
-                    if (syntax.op2EscPBraceCircumflexNot()) {
-                        fetch();
-                        if (c == '^') {
-                            token.setPropNot(!token.getPropNot()); 
-                        } else {
-                            unfetch();
-                        }
-                    }
+            } else {
+                syntaxWarn(Warnings.INVALID_BACKREFERENCE);
+            }
+        }
+    }
+
+    private void fetchTokenFor_subexpCall() {
+        if (syntax.op2EscGSubexpCall()) {
+            if (left()) {
+                fetch();
+                if (c == '<' || c == '\'') {
+                    int last = p;
+                    int gNum = fetchName(c, true);
+                    int nameEnd = value;
+                    token.type = TokenType.CALL;
+                    token.setCallNameP(last);
+                    token.setCallNameEnd(nameEnd);
+                    token.setCallGNum(gNum);
+                } else {
+                    unfetch();
+                    syntaxWarn(Warnings.INVALID_SUBEXP_CALL);
                 }
-                break;
-                
-            default:
-                unfetch();
-                num = fetchEscapedValue();
+            } else {
+                syntaxWarn(Warnings.INVALID_SUBEXP_CALL);
+            }
+        }
+    }
 
-                /* set_raw: */
-                if (token.getC() != num) {
-                    token.type = TokenType.CODE_POINT;
-                    token.setCode(num);
-                } else { /* string */
-                    p = token.backP + enc.length(bytes, token.backP, stop);
+    private void fetchTokenFor_charProperty() {
+        if (peekIs('{') && syntax.op2EscPBraceCharProperty()) {
+            inc();
+            token.type = TokenType.CHAR_PROPERTY;
+            token.setPropNot(c == 'P');
+
+            if (syntax.op2EscPBraceCircumflexNot()) {
+                fetch();
+                if (c == '^') {
+                    token.setPropNot(!token.getPropNot());
+                } else {
+                    unfetch();
                 }
-                break;
-                
-            } // switch (c)
-            
+            }
         } else {
-            token.setC(c);
-            token.escaped = false;
-            
-            // remove code duplication
-            if (Config.USE_VARIABLE_META_CHARS) {
-                if (c != MetaChar.INEFFECTIVE_META_CHAR && syntax.opVariableMetaCharacters()) {
-                    if (c == syntax.metaCharTable.anyChar) { // goto any_char
-                        token.type = TokenType.ANYCHAR;
-                        break;
-                    } else if (c == syntax.metaCharTable.anyTime) { // goto anytime
-                        token.type = TokenType.OP_REPEAT;
-                        token.setRepeatLower(0);
-                        token.setRepeatUpper(QuantifierNode.REPEAT_INFINITE);
-                        greedyCheck();
-                        break;
-                    }  else if (c == syntax.metaCharTable.zeroOrOneTime) { // goto zero_or_one_time
-                        token.type = TokenType.OP_REPEAT;
-                        token.setRepeatLower(0);
-                        token.setRepeatUpper(1);
-                        greedyCheck();
-                        break;
-                    } else if (c == syntax.metaCharTable.oneOrMoreTime) { // goto one_or_more_time
-                        token.type = TokenType.OP_REPEAT;
-                        token.setRepeatLower(1);
-                        token.setRepeatUpper(QuantifierNode.REPEAT_INFINITE);
-                        greedyCheck();
-                        break;
-                    } else if (c == syntax.metaCharTable.anyCharAnyTime) { // goto one_or_more_time
-                        token.type = TokenType.ANYCHAR_ANYTIME;
-                        break;
-                        // goto out
-                    }
-                }
-            } // USE_VARIABLE_META_CHARS
-            
-            { 
+            syntaxWarn(Warnings.INVALID_UNICODE_PROPERTY, (char)c);
+        }
+    }
+
+    private void fetchTokenFor_metaChars() {
+        if (c == syntax.metaCharTable.anyChar) {
+            token.type = TokenType.ANYCHAR;
+        } else if (c == syntax.metaCharTable.anyTime) {
+            fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
+        }  else if (c == syntax.metaCharTable.zeroOrOneTime) {
+            fetchTokenFor_repeat(0, 1);
+        } else if (c == syntax.metaCharTable.oneOrMoreTime) {
+            fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
+        } else if (c == syntax.metaCharTable.anyCharAnyTime) {
+            token.type = TokenType.ANYCHAR_ANYTIME;
+            // goto out
+        }
+    }
+
+    protected final TokenType fetchToken() {
+        // mark(); // out
+        start:
+        while(true) {
+            if (!left()) {
+                token.type = TokenType.EOT;
+                return token.type;
+            }
+
+            token.type = TokenType.STRING;
+            token.base = 0;
+            token.backP = p;
+
+            fetch();
+
+            if (c == syntax.metaCharTable.esc && !syntax.op2IneffectiveEscape()) { // IS_MC_ESC_CODE(code, syn)
+                if (!left()) newSyntaxException(ERR_END_PATTERN_AT_ESCAPE);
+
+                token.backP = p;
+                fetch();
+
+                token.setC(c);
+                token.escaped = true;
                 switch(c) {
-                
-                case '.':
-                    if (!syntax.opDotAnyChar()) break;
-                    // any_char:
-                    token.type = TokenType.ANYCHAR;
-                    break;
-                    
+
                 case '*':
-                    if (!syntax.opAsteriskZeroInf()) break;
-                    // anytime:
-                    token.type = TokenType.OP_REPEAT;
-                    token.setRepeatLower(0);
-                    token.setRepeatUpper(QuantifierNode.REPEAT_INFINITE);
-                    greedyCheck();
+                    if (syntax.opEscAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
                     break;
-
                 case '+':
-                    if (!syntax.opPlusOneInf()) break;
-                    // one_or_more_time:
-                    token.type = TokenType.OP_REPEAT;
-                    token.setRepeatLower(1);
-                    token.setRepeatUpper(QuantifierNode.REPEAT_INFINITE);
-                    greedyCheck();
+                    if (syntax.opEscPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
                     break;
-                    
-                case '?':                   
-                    if (!syntax.opQMarkZeroOne()) break;
-                    // zero_or_one_time:
-                    token.type = TokenType.OP_REPEAT;
-                    token.setRepeatLower(0);
-                    token.setRepeatUpper(1);
-                    greedyCheck();
+                case '?':
+                    if (syntax.opEscQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
                     break;
-                    
                 case '{':
-                    if (!syntax.opBraceInterval()) break;
-                    switch(fetchRangeQuantifier()) {
-                    case 0:
-                        greedyCheck();
-                        break;
-                    case 2:
-                        if (syntax.fixedIntervalIsGreedyOnly()) {
-                            possessiveCheck();
-                        } else {
-                            greedyCheck();
-                        }
-                        break;
-                    default: /* 1 : normal char */
-                    } // inner switch
+                    if (syntax.opEscBraceInterval()) fetchTokenFor_openBrace();
                     break;
-                    
                 case '|':
-                    if (!syntax.opVBarAlt()) break;
-                    token.type = TokenType.ALT;
+                    if (syntax.opEscVBarAlt()) token.type = TokenType.ALT;
                     break;
-                    
                 case '(':
-                    if (peekIs('?') && syntax.op2QMarkGroupEffect()) {
-                        inc();
-                        if (peekIs('#')) {
-                            fetch();
-                            while (true) {
-                                if (!left()) newSyntaxException(ERR_END_PATTERN_IN_GROUP);
-                                fetch();
-                                if (c == syntax.metaCharTable.esc) {
-                                    if (left()) fetch();
-                                } else {
-                                    if (c == ')') break;
-                                }
-                            }
-                            continue start; // goto start
-                        }
-                        unfetch();
-                    }
-                    
-                    if (!syntax.opLParenSubexp()) break;
-                    token.type = TokenType.SUBEXP_OPEN;
+                    if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
                     break;
-                    
                 case ')':
-                    if (!syntax.opLParenSubexp()) break;
-                    token.type = TokenType.SUBEXP_CLOSE;                    
+                    if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
                     break;
-                    
-                case '^':
-                    if (!syntax.opLineAnchor()) break;
-                    token.type = TokenType.ANCHOR;
-                    token.setSubtype(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
+                case 'w':
+                    if (syntax.opEscWWord()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
                     break;
-                    
-                case '$':
-                    if (!syntax.opLineAnchor()) break;
-                    token.type = TokenType.ANCHOR;
-                    token.setSubtype(isSingleline(env.option) ? AnchorType.SEMI_END_BUF : AnchorType.END_LINE);
+                case 'W':
+                    if (syntax.opEscWWord()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
                     break;
-                    
-                case '[':
-                    if (!syntax.opBracketCC()) break;
-                    token.type = TokenType.CC_CC_OPEN;
+                case 'b':
+                    if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.WORD_BOUND);
                     break;
-                    
-                case ']':
-                    //if (*src > env->pattern)   /* /].../ is allowed. */
-                    //CLOSE_BRACKET_WITHOUT_ESC_WARN(env, (UChar* )"]");
+                case 'B':
+                    if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.NOT_WORD_BOUND);
                     break;
-                    
-                case '#':
-                    if (Option.isExtend(env.option)) {
-                        while (left()) {
-                            fetch();
-                            if (enc.isNewLine(c)) break;
-                        }
-                        continue start; // goto start 
-                        
-                    }
+                case '<':
+                    if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_BEGIN);
+                    break;
+                case '>':
+                    if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_END);
+                    break;
+                case 's':
+                    if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
+                    break;
+                case 'S':
+                    if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
+                    break;
+                case 'd':
+                    if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
+                    break;
+                case 'D':
+                    if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
+                    break;
+                case 'h':
+                    if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
+                    break;
+                case 'H':
+                    if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
+                    break;
+                case 'A':
+                    if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
+                    break;
+                case 'Z':
+                    if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.SEMI_END_BUF);
+                    break;
+                case 'z':
+                    if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
+                    break;
+                case 'G':
+                    if (syntax.opEscCapitalGBeginAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_POSITION);
                     break;
-                    
-                case ' ':
-                case '\t':
-                case '\n':
-                case '\r':
-                case '\f':
-                    if (Option.isExtend(env.option)) {
-                        continue start; // goto start
+                case '`':
+                    if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
+                    break;
+                case '\'':
+                    if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
+                    break;
+                case 'x':
+                    fetchTokenFor_xBrace();
+                    break;
+                case 'u':
+                    fetchTokenFor_uHex();
+                    break;
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    fetchTokenFor_digit();
+                    break;
+                case '0':
+                    fetchTokenFor_zero();
+                    break;
+                case 'k':
+                    if (Config.USE_NAMED_GROUP) fetchTokenFor_namedBackref();
+                    break;
+                case 'g':
+                    if (Config.USE_SUBEXP_CALL) fetchTokenFor_subexpCall();
+                    break;
+                case 'Q':
+                    if (syntax.op2EscCapitalQQuote()) token.type = TokenType.QUOTE_OPEN;
+                    break;
+                case 'p':
+                case 'P':
+                    fetchTokenFor_charProperty();
+                    break;
+
+                default:
+                    unfetch();
+                    int num = fetchEscapedValue();
+
+                    /* set_raw: */
+                    if (token.getC() != num) {
+                        token.type = TokenType.CODE_POINT;
+                        token.setCode(num);
+                    } else { /* string */
+                        p = token.backP + enc.length(bytes, token.backP, stop);
                     }
                     break;
-                    
-                default: // string
+
+                } // switch (c)
+
+            } else {
+                token.setC(c);
+                token.escaped = false;
+
+                if (Config.USE_VARIABLE_META_CHARS && (c != MetaChar.INEFFECTIVE_META_CHAR && syntax.opVariableMetaCharacters())) {
+                    fetchTokenFor_metaChars();
                     break;
-                    
-                } // switch
+                }
+
+                {
+                    switch(c) {
+                    case '.':
+                        if (syntax.opDotAnyChar()) token.type = TokenType.ANYCHAR;
+                        break;
+                    case '*':
+                        if (syntax.opAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
+                        break;
+                    case '+':
+                        if (syntax.opPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
+                        break;
+                    case '?':
+                        if (syntax.opQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
+                        break;
+                    case '{':
+                        if (syntax.opBraceInterval()) fetchTokenFor_openBrace();
+                        break;
+                    case '|':
+                        if (syntax.opVBarAlt()) token.type = TokenType.ALT;
+                        break;
+
+                    case '(':
+                        if (peekIs('?') && syntax.op2QMarkGroupEffect()) {
+                            inc();
+                            if (peekIs('#')) {
+                                fetch();
+                                while (true) {
+                                    if (!left()) newSyntaxException(ERR_END_PATTERN_IN_GROUP);
+                                    fetch();
+                                    if (c == syntax.metaCharTable.esc) {
+                                        if (left()) fetch();
+                                    } else {
+                                        if (c == ')') break;
+                                    }
+                                }
+                                continue start; // goto start
+                            }
+                            unfetch();
+                        }
+
+                        if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
+                        break;
+                    case ')':
+                        if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
+                        break;
+                    case '^':
+                        if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
+                        break;
+                    case '$':
+                        if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.SEMI_END_BUF : AnchorType.END_LINE);
+                        break;
+                    case '[':
+                        if (syntax.opBracketCC()) token.type = TokenType.CC_CC_OPEN;
+                        break;
+                    case ']':
+                        //if (*src > env->pattern)   /* /].../ is allowed. */
+                        //CLOSE_BRACKET_WITHOUT_ESC_WARN(env, (UChar* )"]");
+                        break;
+                    case '#':
+                        if (Option.isExtend(env.option)) {
+                            while (left()) {
+                                fetch();
+                                if (enc.isNewLine(c)) break;
+                            }
+                            continue start; // goto start
+                        }
+                        break;
+
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                    case '\f':
+                        if (Option.isExtend(env.option)) continue start; // goto start
+                        break;
+
+                    default: // string
+                        break;
+
+                    } // switch
+                }
             }
-        }
-        
-        break;
+
+            break;
         } // while
-        return token.type;   
+        return token.type;
     }
-    
+
     private void greedyCheck() {
         if (left() && peekIs('?') && syntax.opQMarkNonGreedy()) {
-            
+
             fetch();
 
             token.setRepeatGreedy(false);
@@ -1351,14 +1227,14 @@ class Lexer extends ScannerSupport {
             possessiveCheck();
         }
     }
-    
+
     private void possessiveCheck() {
-        if (left() && peekIs('+') && 
+        if (left() && peekIs('+') &&
             (syntax.op2PlusPossessiveRepeat() && token.type != TokenType.INTERVAL ||
              syntax.op2PlusPossessiveInterval() && token.type == TokenType.INTERVAL)) {
-            
+
             fetch();
-            
+
             token.setRepeatGreedy(true);
             token.setRepeatPossessive(true);
         } else {
@@ -1382,4 +1258,14 @@ class Lexer extends ScannerSupport {
         newInternalException(ERR_PARSER_BUG);
         return 0; // not reached
     }
+
+    protected final void syntaxWarn(String message, char c) {
+        syntaxWarn(message.replace("<%n>", Character.toString(c)));
+    }
+
+    protected final void syntaxWarn(String message) {
+        if (Config.USE_WARN) {
+            env.reg.warnings.warn(message + ": /" + new String(bytes, getBegin(), getEnd()) + "/");
+        }
+    }
 }
diff --git a/src/org/joni/Matcher.java b/src/org/joni/Matcher.java
index 1f0a1dd..4fa923d 100644
--- a/src/org/joni/Matcher.java
+++ b/src/org/joni/Matcher.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 
@@ -59,7 +59,7 @@ public abstract class Matcher extends IntHolder {
     }
 
     // main matching method
-    protected abstract int matchAt(int range, int sstart, int sprev);    
+    protected abstract int matchAt(int range, int sstart, int sprev);
 
     protected abstract void stateCheckBuffInit(int strLength, int offset, int stateNum);
     protected abstract void stateCheckBuffClear();
@@ -67,15 +67,15 @@ public abstract class Matcher extends IntHolder {
     public final Region getRegion() {
         return msaRegion;
     }
-    
+
     public final Region getEagerRegion() {
         return msaRegion != null ? msaRegion : new Region(msaBegin, msaEnd);
     }
-    
+
     public final int getBegin() {
         return msaBegin;
     }
-    
+
     public final int getEnd() {
         return msaEnd;
     }
@@ -84,25 +84,25 @@ public abstract class Matcher extends IntHolder {
         msaOptions = option;
         msaStart = start;
         if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) msaBestLen = -1;
-    }    
+    }
 
     public final int match(int at, int range, int option) {
         msaInit(option, at);
-        
+
         if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
             int offset = at = str;
             stateCheckBuffInit(end - str, offset, regex.numCombExpCheck); // move it to construction?
         } // USE_COMBINATION_EXPLOSION_CHECK
-        
+
         int prev = enc.prevCharHead(bytes, str, at, end);
-        
+
         if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
             return matchAt(end /*range*/, at, prev);
         } else {
             return matchAt(range /*range*/, at, prev);
         }
     }
-    
+
     int low, high; // these are the return values
     private boolean forwardSearchRange(byte[]bytes, int str, int end, int s, int range, IntHolder lowPrev) {
         int pprev = -1;
@@ -114,20 +114,20 @@ public abstract class Matcher extends IntHolder {
                                 ", end: " + end +
                                 ", s: " + s +
                                 ", range: " + range);
-        }        
-        
+        }
+
         if (regex.dMin > 0) {
             if (enc.isSingleByte()) {
                 p += regex.dMin;
             } else {
                 int q = p + regex.dMin;
-                while (p < q) p += enc.length(bytes, p, end);
+                while (p < q && p < end) p += enc.length(bytes, p, end);
             }
         }
 
         retry:while (true) {
             p = regex.searchAlgorithm.search(regex, bytes, p, end, range);
-            
+
             if (p != -1 && p < range) {
                 if (p - regex.dMin < s) {
                     // retry_gate:
@@ -135,7 +135,7 @@ public abstract class Matcher extends IntHolder {
                     p += enc.length(bytes, p, end);
                     continue retry;
                 }
-                
+
                 if (regex.subAnchor != 0) {
                     switch (regex.subAnchor) {
                     case AnchorType.BEGIN_LINE:
@@ -149,7 +149,7 @@ public abstract class Matcher extends IntHolder {
                             }
                         }
                         break;
-                        
+
                     case AnchorType.END_LINE:
                         if (p == end) {
                             if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
@@ -199,30 +199,30 @@ public abstract class Matcher extends IntHolder {
                 }
                 /* no needs to adjust *high, *high is used as range check only */
                 high = p - regex.dMin;
-                
+
                 if (Config.DEBUG_SEARCH) {
                     Config.log.println("forward_search_range success: "+
                                         "low: " + (low - str) +
                                         ", high: " + (high - str) +
                                         ", dmin: " + regex.dMin +
                                         ", dmax: " + regex.dMax);
-                } 
-                
+                }
+
                 return true;    /* success */
             }
 
             return false;   /* fail */
-        } //while            
+        } //while
     }
-    
+
     // low, high
     private boolean backwardSearchRange(byte[]bytes, int str, int end, int s, int range, int adjrange) {
         range += regex.dMin;
         int p = s;
-        
+
         retry:while (true) {
             p = regex.searchAlgorithm.searchBackward(regex, bytes, range, adjrange, end, p, s, range);
-            
+
             if (p != -1) {
                 if (regex.subAnchor != 0) {
                     switch (regex.subAnchor) {
@@ -235,7 +235,7 @@ public abstract class Matcher extends IntHolder {
                             }
                         }
                         break;
-                        
+
                     case AnchorType.END_LINE:
                         if (p == end) {
                             if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
@@ -254,28 +254,28 @@ public abstract class Matcher extends IntHolder {
                         break;
                     } // switch
                 }
-                
+
                 /* no needs to adjust *high, *high is used as range check only */
                 if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
                     low = p - regex.dMax;
                     high = p - regex.dMin;
                     high = enc.rightAdjustCharHead(bytes, adjrange, high, end);
                 }
-                
+
                 if (Config.DEBUG_SEARCH) {
                     Config.log.println("backward_search_range: "+
                                         "low: " + (low - str) +
                                         ", high: " + (high - str));
-                } 
-                
+                }
+
                 return true;
             }
-            
+
             if (Config.DEBUG_SEARCH) Config.log.println("backward_search_range: fail.");
             return false;
         } // while
     }
-    
+
     // MATCH_AND_RETURN_CHECK
     private boolean matchCheck(int upperRange, int s, int prev) {
         if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
@@ -297,30 +297,30 @@ public abstract class Matcher extends IntHolder {
             } else {
                 //range = upperRange;
                 if (matchAt(end, s, prev) != -1) return true;
-            }            
+            }
         }
         return false;
     }
-    
+
     public final int search(int start, int range, int option) {
         int s, prev;
         int origStart = start;
         int origRange = range;
-        
+
         if (Config.DEBUG_SEARCH) {
             Config.log.println("onig_search (entry point): "+
-            		"str: " + str + 
-                    ", end: " + (end - str) + 
-                    ", start: " + (start - str) + 
+            		"str: " + str +
+                    ", end: " + (end - str) +
+                    ", start: " + (start - str) +
                     ", range " + (range - str));
         }
-        
+
         if (start > end || start < str) return -1;
-        
+
         /* anchor optimize: resume search range */
         if (regex.anchor != 0 && str < end) {
             int minSemiEnd, maxSemiEnd;
-            
+
             if ((regex.anchor & AnchorType.BEGIN_POSITION) != 0) {
                 /* search start-position only */
                 // !begin_position:!
@@ -334,7 +334,7 @@ public abstract class Matcher extends IntHolder {
                 if (range > start) {
                     if (start != str) return -1; // mismatch_no_msa;
                     range = str + 1;
-                } else { 
+                } else {
                     if (range <= str) {
                         start = str;
                         range = str;
@@ -359,12 +359,12 @@ public abstract class Matcher extends IntHolder {
                     }
                     if (minSemiEnd > str && start <= minSemiEnd) {
                         // !goto end_buf;!
-                        if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;                        
+                        if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
                     }
                 } else {
                     minSemiEnd = end;
                     // !goto end_buf;!
-                    if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;                    
+                    if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
                 }
             } else if ((regex.anchor & AnchorType.ANYCHAR_STAR_ML) != 0) {
                 // goto !begin_position;!
@@ -372,15 +372,15 @@ public abstract class Matcher extends IntHolder {
                     range = start + 1;
                 } else {
                     range = start;
-                }                
+                }
             }
-            
+
         } else if (str == end) { /* empty string */
             // empty address ?
             if (Config.DEBUG_SEARCH) {
                 Config.log.println("onig_search: empty string.");
             }
-            
+
             if (regex.thresholdLength == 0) {
                 s = start = str;
                 prev = -1;
@@ -393,11 +393,11 @@ public abstract class Matcher extends IntHolder {
             }
             return -1; // goto mismatch_no_msa;
         }
-        
+
         if (Config.DEBUG_SEARCH) {
             Config.log.println("onig_search(apply anchor): " +
                                 "end: " + (end - str) +
-                                ", start " + (start - str) + 
+                                ", start " + (start - str) +
                                 ", range " + (range - str));
         }
 
@@ -406,7 +406,7 @@ public abstract class Matcher extends IntHolder {
             int offset = Math.min(start, range) - str;
             stateCheckBuffInit(end - str, offset, regex.numCombExpCheck);
         }
-        
+
         s = start;
         if (range > start) {    /* forward search */
             if (s > str) {
@@ -414,7 +414,7 @@ public abstract class Matcher extends IntHolder {
             } else {
                 prev = 0; // -1
             }
-            
+
             if (regex.searchAlgorithm != SearchAlgorithm.NONE) {
                 int schRange = range;
                 if (regex.dMax != 0) {
@@ -426,7 +426,7 @@ public abstract class Matcher extends IntHolder {
                     }
                 }
                 if ((end - start) < regex.thresholdLength) return mismatch();
-                
+
                 if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
                     do {
                         if (!forwardSearchRange(bytes, str, end, s, schRange, this)) return mismatch(); // low, high, lowPrev
@@ -441,43 +441,38 @@ public abstract class Matcher extends IntHolder {
                         }
                     } while (s < range);
                     return mismatch();
-                    
+
                 } else { /* check only. */
                     if (!forwardSearchRange(bytes, str, end, s, schRange, null)) return mismatch();
-                    
+
                     if ((regex.anchor & AnchorType.ANYCHAR_STAR) != 0) {
                         do {
                             if (matchCheck(origRange, s, prev)) return match(s);
                             prev = s;
                             s += enc.length(bytes, s, end);
-                            
-                            while (!enc.isNewLine(bytes, prev, end) && s < range) {
-                                prev = s;
-                                s += enc.length(bytes, s, end);
-                            }
                         } while (s < range);
                         return mismatch();
                     }
-                    
+
                 }
             }
-            
+
             do {
                 if (matchCheck(origRange, s, prev)) return match(s);
                 prev = s;
                 s += enc.length(bytes, s, end);
             } while (s < range);
-            
+
             if (s == range) { /* because empty match with /$/. */
                 if (matchCheck(origRange, s, prev)) return match(s);
             }
         } else { /* backward search */
-            if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {            
+            if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
                 if (origStart < end) {
-                    origStart += enc.length(bytes, origStart, end); // /* is upper range */ 
+                    origStart += enc.length(bytes, origStart, end); // /* is upper range */
                 }
             }
-            
+
             if (regex.searchAlgorithm != SearchAlgorithm.NONE) {
                 int adjrange;
                 if (range < end) {
@@ -500,7 +495,7 @@ public abstract class Matcher extends IntHolder {
                     return mismatch();
                 } else { /* check only. */
                     if ((end - range) < regex.thresholdLength) return mismatch();
-                    
+
                     int schStart = s;
                     if (regex.dMax != 0) {
                         if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
@@ -517,20 +512,20 @@ public abstract class Matcher extends IntHolder {
                     if (!backwardSearchRange(bytes, str, end, schStart, range, adjrange)) return mismatch();
                 }
             }
-            
+
             do {
                 prev = enc.prevCharHead(bytes, str, s, end);
                 if (matchCheck(origStart, s, prev)) return match(s);
                 s = prev;
             } while (s >= range);
-            
+
         }
         return mismatch();
     }
-    
+
     private boolean endBuf(int start, int range, int minSemiEnd, int maxSemiEnd) {
         if ((maxSemiEnd - str) < regex.anchorDmin) return true; // mismatch_no_msa;
-        
+
         if (range > start) {
             if ((minSemiEnd - start) > regex.anchorDmax) {
                 start = minSemiEnd - regex.anchorDmax;
@@ -548,7 +543,7 @@ public abstract class Matcher extends IntHolder {
             if ((minSemiEnd - range) > regex.anchorDmax) {
                 range = minSemiEnd - regex.anchorDmax;
             }
-            if ((maxSemiEnd - start) < regex.anchorDmin) {                    
+            if ((maxSemiEnd - start) < regex.anchorDmin) {
                 start = maxSemiEnd - regex.anchorDmin;
                 start = enc.leftAdjustCharHead(bytes, str, start, end);
             }
@@ -556,11 +551,11 @@ public abstract class Matcher extends IntHolder {
         }
         return false;
     }
-    
+
     private int match(int s) {
         return s - str; // sstart ???
     }
-    
+
     private int mismatch() {
         if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
             if (msaBestLen >= 0) {
diff --git a/src/org/joni/MatcherFactory.java b/src/org/joni/MatcherFactory.java
index 729eeb0..09d6083 100644
--- a/src/org/joni/MatcherFactory.java
+++ b/src/org/joni/MatcherFactory.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
diff --git a/src/org/joni/MinMaxLen.java b/src/org/joni/MinMaxLen.java
index dca90e6..072d5f3 100644
--- a/src/org/joni/MinMaxLen.java
+++ b/src/org/joni/MinMaxLen.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -25,40 +25,40 @@ final class MinMaxLen {
 
     MinMaxLen() {
     }
-    
+
     MinMaxLen(int min, int max) {
         this.min = min;
         this.max = max;
     }
-    
-    /* 1000 / (min-max-dist + 1) */    
-    private static final short distValues[] = {     
-        1000,  500,  333,  250,  200,  167,  143,  125,  111,  100, 
-          91,   83,   77,   71,   67,   63,   59,   56,   53,   50, 
-          48,   45,   43,   42,   40,   38,   37,   36,   34,   33, 
-          32,   31,   30,   29,   29,   28,   27,   26,   26,   25, 
-          24,   24,   23,   23,   22,   22,   21,   21,   20,   20, 
-          20,   19,   19,   19,   18,   18,   18,   17,   17,   17, 
-          16,   16,   16,   16,   15,   15,   15,   15,   14,   14, 
-          14,   14,   14,   14,   13,   13,   13,   13,   13,   13, 
-          12,   12,   12,   12,   12,   12,   11,   11,   11,   11, 
+
+    /* 1000 / (min-max-dist + 1) */
+    private static final short distValues[] = {
+        1000,  500,  333,  250,  200,  167,  143,  125,  111,  100,
+          91,   83,   77,   71,   67,   63,   59,   56,   53,   50,
+          48,   45,   43,   42,   40,   38,   37,   36,   34,   33,
+          32,   31,   30,   29,   29,   28,   27,   26,   26,   25,
+          24,   24,   23,   23,   22,   22,   21,   21,   20,   20,
+          20,   19,   19,   19,   18,   18,   18,   17,   17,   17,
+          16,   16,   16,   16,   15,   15,   15,   15,   14,   14,
+          14,   14,   14,   14,   13,   13,   13,   13,   13,   13,
+          12,   12,   12,   12,   12,   12,   11,   11,   11,   11,
           11,   11,   11,   11,   11,   10,   10,   10,   10,   10
     };
-    
+
     int distanceValue() {
         if (max == INFINITE_DISTANCE) return 0;
         int d = max - min;
         /* return dist_vals[d] * 16 / (mm->min + 12); */
-        return d < distValues.length ? distValues[d] : 1; 
+        return d < distValues.length ? distValues[d] : 1;
     }
-    
+
     int compareDistanceValue(MinMaxLen other, int v1, int v2) {
         if (v2 <= 0) return -1;
         if (v1 <= 0) return 1;
-        
+
         v1 *= distanceValue();
         v2 *= other.distanceValue();
-        
+
         if (v2 > v1) return 1;
         if (v2 < v1) return -1;
 
@@ -68,38 +68,38 @@ final class MinMaxLen {
     }
 
     boolean equal(MinMaxLen other) {
-        return min == other.min && max == other.max;        
+        return min == other.min && max == other.max;
     }
 
     void set(int min, int max) {
         this.min = min;
         this.max = max;
     }
-    
+
     void clear() {
         min = max = 0;
     }
-    
-    void copy(MinMaxLen other) { 
+
+    void copy(MinMaxLen other) {
         min = other.min;
         max = other.max;
     }
-    
+
     void add(MinMaxLen other) {
         min = distanceAdd(min, other.min);
         max = distanceAdd(max, other.max);
     }
-    
+
     void addLength(int len) {
         min = distanceAdd(min, len);
-        max = distanceAdd(max, len);        
+        max = distanceAdd(max, len);
     }
 
     void altMerge(MinMaxLen other) {
         if (min > other.min) min = other.min;
-        if (max < other.max) max = other.max;        
+        if (max < other.max) max = other.max;
     }
-    
+
     static final int INFINITE_DISTANCE = 0x7FFFFFFF;
     static int distanceAdd(int d1, int d2) {
         if (d1 == INFINITE_DISTANCE || d2 == INFINITE_DISTANCE) {
@@ -118,7 +118,7 @@ final class MinMaxLen {
             return INFINITE_DISTANCE;
         }
     }
-    
+
     static String distanceRangeToString(int a, int b) {
         String s = "";
         if (a == INFINITE_DISTANCE) {
@@ -128,12 +128,12 @@ final class MinMaxLen {
         }
 
         s += "-";
-        
+
         if (b == INFINITE_DISTANCE) {
             s += "inf";
         } else {
             s += "(" + b + ")";
         }
-        return s;        
+        return s;
     }
 }
diff --git a/src/org/joni/NameEntry.java b/src/org/joni/NameEntry.java
index 794cf1b..55c34b3 100644
--- a/src/org/joni/NameEntry.java
+++ b/src/org/joni/NameEntry.java
@@ -1,35 +1,35 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
 
 public final class NameEntry {
     static final int INIT_NAME_BACKREFS_ALLOC_NUM = 8;
-    
+
     public final byte[]name;
     public final int nameP;
     public final int nameEnd;
-    
+
     int backNum;
     int backRef1;
     int backRefs[];
-    
+
     public NameEntry(byte[]bytes, int p, int end) {
         name = bytes;
         nameP = p;
@@ -52,18 +52,18 @@ public final class NameEntry {
     private void alloc() {
         backRefs = new int[INIT_NAME_BACKREFS_ALLOC_NUM];
     }
-    
+
     private void ensureSize() {
         if (backNum > backRefs.length) {
-            int[]tmp = new int[backRefs.length << 1];           
+            int[]tmp = new int[backRefs.length << 1];
             System.arraycopy(backRefs, 0, tmp, 0, backRefs.length);
             backRefs = tmp;
         }
     }
-    
+
     public void addBackref(int backRef) {
         backNum++;
-        
+
         switch (backNum) {
             case 1:
                 backRef1 = backRef;
@@ -78,7 +78,7 @@ public final class NameEntry {
                 backRefs[backNum - 1] = backRef;
         }
     }
-    
+
     public String toString() {
         StringBuilder buff = new StringBuilder(new String(name, nameP, nameEnd - nameP) + " ");
         if (backNum == 0) {
@@ -91,7 +91,7 @@ public final class NameEntry {
                 buff.append(backRefs[i]);
             }
         }
-        return buff.toString();     
+        return buff.toString();
     }
 
 }
diff --git a/src/org/joni/NativeMachine.java b/src/org/joni/NativeMachine.java
index 6fc5dbb..3c37ad3 100644
--- a/src/org/joni/NativeMachine.java
+++ b/src/org/joni/NativeMachine.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
diff --git a/src/org/joni/NodeOptInfo.java b/src/org/joni/NodeOptInfo.java
index 20bc0bc..1943666 100644
--- a/src/org/joni/NodeOptInfo.java
+++ b/src/org/joni/NodeOptInfo.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -28,13 +28,13 @@ public final class NodeOptInfo {
     final OptExactInfo exm = new OptExactInfo();            /* middle */
     final OptExactInfo expr = new OptExactInfo();           /* prec read (?=...) */
     final OptMapInfo map = new OptMapInfo();                /* boundary */
-    
+
     public void setBoundNode(MinMaxLen mmd) {
         exb.mmd.copy(mmd);
         expr.mmd.copy(mmd);
         map.mmd.copy(mmd);
-    }    
-    
+    }
+
     public void clear() {
         length.clear();
         anchor.clear();
@@ -43,7 +43,7 @@ public final class NodeOptInfo {
         expr.clear();
         map.clear();
     }
-    
+
     public void copy(NodeOptInfo other) {
         length.copy(other.length);
         anchor.copy(other.anchor);
@@ -52,40 +52,40 @@ public final class NodeOptInfo {
         expr.copy(other.expr);
         map.copy(other.map);
     }
-    
+
     public void concatLeftNode(NodeOptInfo other, Encoding enc) {
         OptAnchorInfo tanchor = new OptAnchorInfo(); // remove it somehow ?
         tanchor.concat(anchor, other.anchor, length.max, other.length.max);
         anchor.copy(tanchor);
-        
+
         if (other.exb.length > 0 && length.max == 0) {
             tanchor.concat(anchor, other.exb.anchor, length.max, other.length.max);
             other.exb.anchor.copy(tanchor);
         }
-        
+
         if (other.map.value > 0 && length.max == 0) {
             if (other.map.mmd.max == 0) {
                 other.map.anchor.leftAnchor |= anchor.leftAnchor;
             }
         }
-        
+
         boolean exbReach = exb.reachEnd;
         boolean exmReach = exm.reachEnd;
-        
+
         if (other.length.max != 0) {
             exb.reachEnd = exm.reachEnd = false;
         }
-        
+
         if (other.exb.length > 0) {
             if (exbReach) {
                 exb.concat(other.exb, enc);
                 other.exb.clear();
-            } else if (exmReach) {            	
-            	exm.concat(other.exb, enc);            	
-            	other.exb.clear();            	
+            } else if (exmReach) {
+            	exm.concat(other.exb, enc);
+            	other.exb.clear();
             }
         }
-        
+
         exm.select(other.exb, enc);
         exm.select(other.exm, enc);
 
@@ -108,7 +108,7 @@ public final class NodeOptInfo {
         map.select(other.map);
         length.add(other.length);
     }
-    
+
     public void altMerge(NodeOptInfo other, OptEnvironment env) {
         anchor.altMerge(other.anchor);
         exb.altMerge(other.exb, env);
@@ -123,5 +123,5 @@ public final class NodeOptInfo {
         expr.mmd.copy(mmd);
         map.mmd.copy(mmd);
     }
-    
+
 }
diff --git a/src/org/joni/OptAnchorInfo.java b/src/org/joni/OptAnchorInfo.java
index 9084728..202ba81 100644
--- a/src/org/joni/OptAnchorInfo.java
+++ b/src/org/joni/OptAnchorInfo.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -24,16 +24,16 @@ import org.joni.constants.AnchorType;
 final class OptAnchorInfo implements AnchorType {
     int leftAnchor;
     int rightAnchor;
-    
+
     void clear() {
         leftAnchor = rightAnchor = 0;
     }
-    
+
     void copy(OptAnchorInfo other) {
         leftAnchor = other.leftAnchor;
         rightAnchor = other.rightAnchor;
     }
-    
+
     void concat(OptAnchorInfo left, OptAnchorInfo right, int leftLength, int rightLength) {
         leftAnchor = left.leftAnchor;
         if (leftLength == 0) leftAnchor |= right.leftAnchor;
@@ -41,12 +41,12 @@ final class OptAnchorInfo implements AnchorType {
         rightAnchor = right.rightAnchor;
         if (rightLength == 0) rightAnchor |= left.rightAnchor;
     }
-    
+
     boolean isSet(int anchor) {
         if ((leftAnchor & anchor) != 0) return true;
-        return (rightAnchor & anchor) != 0; 
+        return (rightAnchor & anchor) != 0;
     }
-    
+
     void add(int anchor) {
         if (isLeftAnchor(anchor)) {
             leftAnchor |= anchor;
@@ -54,7 +54,7 @@ final class OptAnchorInfo implements AnchorType {
             rightAnchor |= anchor;
         }
     }
-    
+
     void remove(int anchor) {
         if (isLeftAnchor(anchor)) {
             leftAnchor &= ~anchor;
@@ -62,18 +62,18 @@ final class OptAnchorInfo implements AnchorType {
             rightAnchor &= ~anchor;
         }
     }
-    
+
     void altMerge(OptAnchorInfo other) {
         leftAnchor &= other.leftAnchor;
         rightAnchor &= other.rightAnchor;
     }
-    
+
     static boolean isLeftAnchor(int anchor) { // make a mask for it ?
         return !(anchor == END_BUF || anchor == SEMI_END_BUF ||
                  anchor == END_LINE || anchor == PREC_READ ||
                  anchor == PREC_READ_NOT);
     }
-    
+
     static String anchorToString(int anchor) {
         StringBuffer s = new StringBuffer("[");
 
diff --git a/src/org/joni/OptEnvironment.java b/src/org/joni/OptEnvironment.java
index 4b59c31..bc62750 100644
--- a/src/org/joni/OptEnvironment.java
+++ b/src/org/joni/OptEnvironment.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -28,7 +28,7 @@ final class OptEnvironment {
     int options;
     int caseFoldFlag;
     ScanEnvironment scanEnv;
-    
+
     void copy(OptEnvironment other) {
         mmd.copy(other.mmd);
         enc = other.enc;
diff --git a/src/org/joni/OptExactInfo.java b/src/org/joni/OptExactInfo.java
index 45d94fc..ef9fb78 100644
--- a/src/org/joni/OptExactInfo.java
+++ b/src/org/joni/OptExactInfo.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -23,55 +23,54 @@ import org.jcodings.Encoding;
 
 final class OptExactInfo {
     static final int OPT_EXACT_MAXLEN = 24;
-    
+
     final MinMaxLen mmd = new MinMaxLen();
     final OptAnchorInfo anchor = new OptAnchorInfo();
-    
+
     boolean reachEnd;
     boolean ignoreCase;
+
+    final byte bytes[] = new byte[OPT_EXACT_MAXLEN];
     int length;
 
-    final byte s[] = new byte[OPT_EXACT_MAXLEN];
-    
     boolean isFull() {
-        return length >= OPT_EXACT_MAXLEN; 
+        return length >= OPT_EXACT_MAXLEN;
     }
-    
+
     void clear() {
         mmd.clear();
         anchor.clear();
-        
+
         reachEnd = false;
         ignoreCase = false;
         length = 0;
-        s[0] = 0; // ???
     }
-    
+
     void copy(OptExactInfo other) {
         mmd.copy(other.mmd);
         anchor.copy(other.anchor);
         reachEnd = other.reachEnd;
         ignoreCase = other.ignoreCase;
         length = other.length;
-        
-        System.arraycopy(other.s, 0, s, 0, OPT_EXACT_MAXLEN);
+
+        System.arraycopy(other.bytes, 0, bytes, 0, OPT_EXACT_MAXLEN);
     }
-    
+
     void concat(OptExactInfo other, Encoding enc) {
         if (!ignoreCase && other.ignoreCase) {
             if (length >= other.length) return; /* avoid */
-            ignoreCase = true;            
+            ignoreCase = true;
         }
-        
+
         int p = 0; // add->s;
         int end = p + other.length;
-        
+
         int i;
         for (i=length; p < end;) {
-            int len = enc.length(other.s, p, end);
+            int len = enc.length(other.bytes, p, end);
             if (i + len > OPT_EXACT_MAXLEN) break;
             for (int j=0; j<len && p < end; j++) {
-                s[i++] = other.s[p++]; // arraycopy or even don't copy anything ??
+                bytes[i++] = other.bytes[p++]; // arraycopy or even don't copy anything ??
             }
         }
 
@@ -83,18 +82,18 @@ final class OptExactInfo {
         if (!other.reachEnd) tmp.rightAnchor = 0;
         anchor.copy(tmp);
     }
-    
+
     // ?? raw is not used here
-    void concatStr(byte[]bytes, int p, int end, boolean raw, Encoding enc) {
+    void concatStr(byte[]lbytes, int p, int end, boolean raw, Encoding enc) {
         int i;
         for (i = length; p < end && i < OPT_EXACT_MAXLEN;) {
-            int len = enc.length(bytes, p, end);
+            int len = enc.length(lbytes, p, end);
             if (i + len > OPT_EXACT_MAXLEN) break;
             for (int j=0; j<len && p < end; j++) {
-                s[i++] = bytes[p++];
+                bytes[i++] = lbytes[p++];
             }
         }
-        
+
         length = i;
     }
 
@@ -103,41 +102,41 @@ final class OptExactInfo {
             clear();
             return;
         }
-        
+
         if (!mmd.equal(other.mmd)) {
             clear();
             return;
         }
-        
+
         int i;
         for (i=0; i<length && i<other.length;) {
-            if (s[i] != other.s[i]) break;
-            int len = env.enc.length(s, i, length);
+            if (bytes[i] != other.bytes[i]) break;
+            int len = env.enc.length(bytes, i, length);
 
             int j;
             for (j=1; j<len; j++) {
-                if (s[i+j] != other.s[i+j]) break;
+                if (bytes[i+j] != other.bytes[i+j]) break;
             }
-            
+
             if (j < len) break;
             i += len;
         }
-        
+
         if (!other.reachEnd || i<other.length || i<length) reachEnd = false;
-        
+
         length = i;
         ignoreCase |= other.ignoreCase;
-        
+
         anchor.altMerge(other.anchor);
-        
+
         if (!reachEnd) anchor.rightAnchor = 0;
     }
-    
-    
+
+
     void select(OptExactInfo alt, Encoding enc) {
         int v1 = length;
         int v2 = alt.length;
-        
+
         if (v2 == 0) {
             return;
         } else if (v1 == 0) {
@@ -145,27 +144,27 @@ final class OptExactInfo {
             return;
         } else if (v1 <= 2 && v2 <= 2) {
             /* ByteValTable[x] is big value --> low price */
-            v2 = OptMapInfo.positionValue(enc, s[0] & 0xff);
-            v1 = OptMapInfo.positionValue(enc, alt.s[0] & 0xff);
-            
+            v2 = OptMapInfo.positionValue(enc, bytes[0] & 0xff);
+            v1 = OptMapInfo.positionValue(enc, alt.bytes[0] & 0xff);
+
             if (length > 1) v1 += 5;
             if (alt.length > 1) v2 += 5;
         }
-        
+
         if (!ignoreCase) v1 *= 2;
         if (!alt.ignoreCase) v2 *= 2;
-        
+
         if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) copy(alt);
     }
-    
+
     // comp_opt_exact_or_map_info
     private static final int COMP_EM_BASE   = 20;
     int compare(OptMapInfo m) {
         if (m.value <= 0) return -1;
-        
+
         int ve = COMP_EM_BASE * length * (ignoreCase ? 1 : 2);
         int vm = COMP_EM_BASE * 5 * 2 / m.value;
-        
+
         return mmd.compareDistanceValue(m.mmd, ve, vm);
     }
 }
diff --git a/src/org/joni/OptMapInfo.java b/src/org/joni/OptMapInfo.java
index 2763b53..9d3574b 100644
--- a/src/org/joni/OptMapInfo.java
+++ b/src/org/joni/OptMapInfo.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -23,20 +23,20 @@ import org.jcodings.CaseFoldCodeItem;
 import org.jcodings.Encoding;
 
 final class OptMapInfo {
-    
+
     final MinMaxLen mmd = new MinMaxLen();          /* info position */
     final OptAnchorInfo anchor = new OptAnchorInfo();
-    
+
     int value;                                      /* weighted value */
     final byte map[] = new byte[Config.CHAR_TABLE_SIZE];
-    
+
     void clear() {
         mmd.clear();
         anchor.clear();
         value = 0;
         for (int i=0; i<map.length; i++) map[i] = 0;
     }
-    
+
     void copy(OptMapInfo other) {
         mmd.copy(other.mmd);
         anchor.copy(other.anchor);
@@ -44,7 +44,7 @@ final class OptMapInfo {
         //for(int i=0; i<map.length; i++) map[i] = other.map[i];
         System.arraycopy(other.map, 0, map, 0, other.map.length);
     }
-    
+
     void addChar(byte c, Encoding enc) {
         int c_ = c & 0xff;
         if (map[c_] == 0) {
@@ -52,10 +52,10 @@ final class OptMapInfo {
             value += positionValue(enc, c_);
         }
     }
-    
+
     void addCharAmb(byte[]bytes, int p, int end, Encoding enc, int caseFoldFlag) {
         addChar(bytes[p], enc);
-        
+
         caseFoldFlag &= ~Config.INTERNAL_ENC_CASE_FOLD_MULTI_CHAR;
         CaseFoldCodeItem[]items = enc.caseFoldCodesByString(caseFoldFlag, bytes, p, end);
 
@@ -74,13 +74,13 @@ final class OptMapInfo {
             copy(alt);
             return;
         }
-        
+
         int v1 = z / value;
         int v2 = z /alt.value;
-        
+
         if (mmd.compareDistanceValue(alt.mmd, v1, v2) > 0) copy(alt);
     }
-    
+
     // alt_merge_opt_map_info
     void altMerge(OptMapInfo other, Encoding enc) {
         /* if (! is_equal_mml(&to->mmd, &add->mmd)) return ; */
@@ -89,19 +89,19 @@ final class OptMapInfo {
             clear();
             return;
         }
-        
+
         mmd.altMerge(other.mmd);
-        
+
         int val = 0;
         for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
             if (other.map[i] != 0) map[i] = 1;
             if (map[i] != 0) val += positionValue(enc, i);
         }
-        
+
         value = val;
         anchor.altMerge(other.anchor);
     }
-    
+
     static final short ByteValTable[] = {
         5,  1,  1,  1,  1,  1,  1,  1,  1, 10, 10,  1,  1, 10,  1,  1,
         1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
@@ -112,9 +112,9 @@ final class OptMapInfo {
         5,  6,  6,  6,  6,  7,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
         6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  5,  5,  5,  5,  1
      };
-    
+
     // map_position_value
-    static int positionValue(Encoding enc, int i) { 
+    static int positionValue(Encoding enc, int i) {
         if (i < ByteValTable.length) {
             if (i == 0 && enc.minLength() > 1) {
                 return 20;
@@ -125,5 +125,5 @@ final class OptMapInfo {
             return 4; /* Take it easy. */
         }
     }
-    
+
 }
diff --git a/src/org/joni/Option.java b/src/org/joni/Option.java
index adee24f..13fbba3 100644
--- a/src/org/joni/Option.java
+++ b/src/org/joni/Option.java
@@ -1,42 +1,42 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
 
 public class Option {
-    
+
     /* options */
     public static final int NONE                 = 0;
-    public static final int IGNORECASE           = (1<<0); 
+    public static final int IGNORECASE           = (1<<0);
     public static final int EXTEND               = (1<<1);
     public static final int MULTILINE            = (1<<2);
-    public static final int SINGLELINE           = (1<<3); 
-    public static final int FIND_LONGEST         = (1<<4); 
+    public static final int SINGLELINE           = (1<<3);
+    public static final int FIND_LONGEST         = (1<<4);
     public static final int FIND_NOT_EMPTY       = (1<<5);
     public static final int NEGATE_SINGLELINE    = (1<<6);
     public static final int DONT_CAPTURE_GROUP   = (1<<7);
     public static final int CAPTURE_GROUP        = (1<<8);
-    
+
     /* options (search time) */
     public static final int NOTBOL               = (1<<9);
     public static final int NOTEOL               = (1<<10);
-    public static final int POSIX_REGION         = (1<<11);    
+    public static final int POSIX_REGION         = (1<<11);
     public static final int MAXBIT               = (1<<12); /* limit */
 
     public static final int DEFAULT              = NONE;
@@ -52,67 +52,67 @@ public class Option {
         if (isNegateSingleline(option)) options += "NEGATE_SINGLELINE ";
         if (isDontCaptureGroup(option)) options += "DONT_CAPTURE_GROUP ";
         if (isCaptureGroup(option)) options += "CAPTURE_GROUP ";
-        
+
         if (isNotBol(option)) options += "NOTBOL ";
         if (isNotEol(option)) options += "NOTEOL ";
-        if (isPosixRegion(option)) options += "POSIX_REGION ";        
-        
+        if (isPosixRegion(option)) options += "POSIX_REGION ";
+
         return options;
-    }    
-    
+    }
+
     public static boolean isIgnoreCase(int option) {
         return (option & IGNORECASE) != 0;
     }
-    
+
     public static boolean isExtend(int option) {
         return (option & EXTEND) != 0;
-    }   
+    }
 
     public static boolean isSingleline(int option) {
         return (option & SINGLELINE) != 0;
-    }   
+    }
 
     public static boolean isMultiline(int option) {
         return (option & MULTILINE) != 0;
-    }   
+    }
 
     public static boolean isFindLongest(int option) {
         return (option & FIND_LONGEST) != 0;
-    }   
+    }
 
     public static boolean isFindNotEmpty(int option) {
         return (option & FIND_NOT_EMPTY) != 0;
-    }   
+    }
 
     public static boolean isFindCondition(int option) {
         return (option & (FIND_LONGEST | FIND_NOT_EMPTY)) != 0;
-    }   
-    
+    }
+
     public static boolean isNegateSingleline(int option) {
         return (option & NEGATE_SINGLELINE) != 0;
     }
-    
+
     public static boolean isDontCaptureGroup(int option) {
         return (option & DONT_CAPTURE_GROUP) != 0;
-    }   
-    
+    }
+
     public static boolean isCaptureGroup(int option) {
         return (option & CAPTURE_GROUP) != 0;
-    }   
-    
+    }
+
     public static boolean isNotBol(int option) {
         return (option & NOTBOL) != 0;
-    }   
+    }
 
     public static boolean isNotEol(int option) {
         return (option & NOTEOL) != 0;
     }
-    
+
     public static boolean isPosixRegion(int option) {
         return (option & POSIX_REGION) != 0;
-    }   
- 
-    /* OP_SET_OPTION is required for these options.  ??? */    
+    }
+
+    /* OP_SET_OPTION is required for these options.  ??? */
     //    public static boolean isDynamic(int option) {
     //        return (option & (MULTILINE | IGNORECASE)) != 0;
     //    }
diff --git a/src/org/joni/Parser.java b/src/org/joni/Parser.java
index a787d16..3d56e9e 100644
--- a/src/org/joni/Parser.java
+++ b/src/org/joni/Parser.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -24,6 +24,7 @@ import static org.joni.BitStatus.bsOnOff;
 import static org.joni.Option.isDontCaptureGroup;
 import static org.joni.Option.isIgnoreCase;
 
+import org.jcodings.Ptr;
 import org.jcodings.constants.CharacterType;
 import org.jcodings.constants.PosixBracket;
 import org.joni.ast.AnchorNode;
@@ -49,28 +50,28 @@ class Parser extends Lexer {
 
     protected final Regex regex;
     protected Node root;
-    
+
     protected int returnCode; // return code used by parser methods (they itself return parsed nodes)
-                              // this approach will not affect recursive calls 
-    
+                              // this approach will not affect recursive calls
+
     protected Parser(ScanEnvironment env, byte[]bytes, int p, int end) {
         super(env, bytes, p, end);
         regex = env.reg;
     }
-    
+
     // onig_parse_make_tree
     protected final Node parse() {
         root = parseRegexp();
         regex.numMem = env.numMem;
         return root;
     }
-    
+
     private static final int POSIX_BRACKET_NAME_MIN_LEN            = 4;
     private static final int POSIX_BRACKET_CHECK_LIMIT_LENGTH      = 20;
     private static final byte BRACKET_END[]                        = ":]".getBytes();
     private boolean parsePosixBracket(CClassNode cc) {
         mark();
-        
+
         boolean not;
         if (peekIs('^')) {
             inc();
@@ -94,17 +95,17 @@ class Parser extends Lexer {
                     return false;
                 }
             }
-            
+
         }
 
         // not_posix_bracket:
         c = 0;
         int i= 0;
-        while(left() && ((c=peek()) != ':') && c != ']') {
+        while (left() && ((c=peek()) != ':') && c != ']') {
             inc();
             if (++i > POSIX_BRACKET_CHECK_LIMIT_LENGTH) break;
         }
-        
+
         if (c == ':' && left()) {
             inc();
             if (left()) {
@@ -115,7 +116,7 @@ class Parser extends Lexer {
         restore();
         return true; /* 1: is not POSIX bracket, but no error. */
     }
-    
+
     private CClassNode parseCharProperty() {
         int ctype = fetchCharPropertyToCType();
         CClassNode n = new CClassNode();
@@ -123,76 +124,82 @@ class Parser extends Lexer {
         if (token.getPropNot()) n.setNot();
         return n;
     }
-    
+
     private boolean codeExistCheck(int code, boolean ignoreEscaped) {
         mark();
-        
+
         boolean inEsc = false;
-        while(left()) {
-            if (ignoreEscaped && inEsc) { 
+        while (left()) {
+            if (ignoreEscaped && inEsc) {
                 inEsc = false;
             } else {
                 fetch();
                 if (c == code) {
                     restore();
-                    return true; 
+                    return true;
                 }
                 if (c == syntax.metaCharTable.esc) inEsc = true;
             }
         }
-        
+
         restore();
         return false;
     }
-    
+
     private CClassNode parseCharClass() {
         fetchTokenInCC();
 
-        boolean neg;
+        final boolean neg;
         if (token.type == TokenType.CHAR && token.getC() == '^' && !token.escaped) {
             neg = true;
             fetchTokenInCC();
         } else {
             neg = false;
         }
-        
+
         if (token.type == TokenType.CC_CLOSE) {
             if (!codeExistCheck(']', true)) newSyntaxException(ERR_EMPTY_CHAR_CLASS);
             env.ccEscWarn("]");
             token.type = TokenType.CHAR; /* allow []...] */
         }
-        
+
         CClassNode cc = new CClassNode();
         CClassNode prevCC = null;
         CClassNode workCC = null;
 
         CCStateArg arg = new CCStateArg();
-        
+
         boolean andStart = false;
         arg.state = CCSTATE.START;
 
-        while(token.type != TokenType.CC_CLOSE) {
+        while (token.type != TokenType.CC_CLOSE) {
             boolean fetched = false;
-            
+
             switch (token.type) {
-            
+
             case CHAR:
-                    int len = enc.codeToMbcLength(token.getC());
-                    if (len > 1) { 
+                final int len;
+                if (Config.VANILLA) {
+                    len = enc.codeToMbcLength(token.getC());
+                    if (len > 1) {
                         arg.inType = CCVALTYPE.CODE_POINT;
                     } else {
-                        // !sb_char:!
-                        arg.inType = CCVALTYPE.SB;
+                        arg.inType = CCVALTYPE.SB; // sb_char:
                     }
-                    arg.v = token.getC();
-                    arg.vIsRaw = false;
-                    // !goto val_entry2;!
-                    valEntry2(cc, arg);
+                } else {
+                    if (token.getCode() >= BitSet.SINGLE_BYTE_SIZE || (len = enc.codeToMbcLength(token.getC())) > 1) {
+                        arg.inType = CCVALTYPE.CODE_POINT;
+                    } else {
+                        arg.inType = CCVALTYPE.SB; // sb_char:
+                    }
+                }
+                arg.v = token.getC();
+                arg.vIsRaw = false;
+                parseCharClassValEntry2(cc, arg); // goto val_entry2
                 break;
 
             case RAW_BYTE:
-                /* tok->base != 0 : octal or hexadec. */
-                if (!enc.isSingleByte() && token.base != 0) {
+                if (!enc.isSingleByte() && token.base != 0) { /* tok->base != 0 : octal or hexadec. */
                     byte[]buf = new byte[Config.ENC_MBC_CASE_FOLD_MAXLEN];
                     int psave = p;
                     int base = token.base;
@@ -207,9 +214,9 @@ class Parser extends Lexer {
                         buf[i] = (byte)token.getC();
                     }
                     if (i < enc.minLength()) newValueException(ERR_TOO_SHORT_MULTI_BYTE_STRING);
-                    
+
                     len = enc.length(buf, 0, i);
-                    if (i < len) { 
+                    if (i < len) {
                         newValueException(ERR_TOO_SHORT_MULTI_BYTE_STRING);
                     } else if (i > len) { /* fetch back */
                         p = psave;
@@ -218,120 +225,99 @@ class Parser extends Lexer {
                     }
                     if (i == 1) {
                         arg.v = buf[0] & 0xff;
-                        // !goto raw_single!
-                        arg.inType = CCVALTYPE.SB;
+                        arg.inType = CCVALTYPE.SB; // goto raw_single
                     } else {
                         arg.v = enc.mbcToCode(buf, 0, buf.length);
                         arg.inType = CCVALTYPE.CODE_POINT;
                     }
                 } else {
                     arg.v = token.getC();
-                    // !raw_single:!
-                    arg.inType = CCVALTYPE.SB;
+                    arg.inType = CCVALTYPE.SB; // raw_single:
                 }
                 arg.vIsRaw = true;
-                // !goto val_entry2;!
-                valEntry2(cc, arg);
+                parseCharClassValEntry2(cc, arg); // goto val_entry2
                 break;
-                
+
             case CODE_POINT:
                 arg.v = token.getCode();
                 arg.vIsRaw = true;
-                // !val_entry:!
-                // !val_entry2:!
-                valEntry(cc, arg);
+                parseCharClassValEntry(cc, arg); // val_entry:, val_entry2
                 break;
-                
+
             case POSIX_BRACKET_OPEN:
                 if (parsePosixBracket(cc)) { /* true: is not POSIX bracket */
                     env.ccEscWarn("[");
                     p = token.backP;
                     arg.v = token.getC();
                     arg.vIsRaw = false;
-                    // !goto val_entry;!
-                    valEntry(cc, arg);
+                    parseCharClassValEntry(cc, arg); // goto val_entry
                     break;
                 }
-                // !goto next_class;!
-                cc.nextStateClass(arg, env);
+                cc.nextStateClass(arg, env); // goto next_class
                 break;
-                
+
             case CHAR_TYPE:
                 cc.addCType(token.getPropCType(), token.getPropNot(), env, this);
-                // !next_class:!
-                cc.nextStateClass(arg, env);
+                cc.nextStateClass(arg, env); // next_class:
                 break;
-                
+
             case CHAR_PROPERTY:
                 int ctype = fetchCharPropertyToCType();
                 cc.addCType(ctype, token.getPropNot(), env, this);
-                // !goto next_class;!
-                cc.nextStateClass(arg, env);
+                cc.nextStateClass(arg, env); // goto next_class
                 break;
-                
+
             case CC_RANGE:
                 if (arg.state == CCSTATE.VALUE) {
                     fetchTokenInCC();
                     fetched = true;
                     if (token.type == TokenType.CC_CLOSE) { /* allow [x-] */
-                        // !range_end_val:!
-                        // !goto val_entry;!
-                        rangeEndVal(cc, arg);
+                        parseCharClassRangeEndVal(cc, arg); // range_end_val:, goto val_entry;
                         break;
                     } else if (token.type == TokenType.CC_AND) {
                         env.ccEscWarn("-");
-                        // goto !range_end_val;!
-                        rangeEndVal(cc, arg);
+                        parseCharClassRangeEndVal(cc, arg); // goto range_end_val
                         break;
                     }
                     arg.state = CCSTATE.RANGE;
                 } else if (arg.state == CCSTATE.START) {
-                    /* [-xa] is allowed */
-                    arg.v = token.getC();
+                    arg.v = token.getC(); /* [-xa] is allowed */
                     arg.vIsRaw = false;
                     fetchTokenInCC();
                     fetched = true;
-                    /* [--x] or [a&&-x] is warned. */
-                    if (token.type == TokenType.CC_RANGE || andStart) env.ccEscWarn("-");
-                    // !goto val_entry;!
-                    valEntry(cc, arg);
+                    if (token.type == TokenType.CC_RANGE || andStart) env.ccEscWarn("-"); /* [--x] or [a&&-x] is warned. */
+                    parseCharClassValEntry(cc, arg); // goto val_entry
                     break;
                 } else if (arg.state == CCSTATE.RANGE) {
                     env.ccEscWarn("-");
-                    /* [!--x] is allowed */
-                    // !goto sb_char;!
-                    sbChar(cc, arg);
+                    parseCharClassSbChar(cc, arg); // goto sb_char /* [!--x] is allowed */
                     break;
                 } else { /* CCS_COMPLETE */
                     fetchTokenInCC();
                     fetched = true;
                     if (token.type == TokenType.CC_CLOSE) { /* allow [a-b-] */
-                        // goto !range_end_val!
-                        rangeEndVal(cc, arg);
+                        parseCharClassRangeEndVal(cc, arg); // goto range_end_val
                         break;
                     } else if (token.type == TokenType.CC_AND) {
                         env.ccEscWarn("-");
-                        // goto !range_end_val;!
-                        rangeEndVal(cc, arg);
+                        parseCharClassRangeEndVal(cc, arg); // goto range_end_val
                         break;
                     }
-                    
+
                     if (syntax.allowDoubleRangeOpInCC()) {
                         env.ccEscWarn("-");
-                        /* [0-9-a] is allowed as [0-9\-a] */
-                        // !goto sb_char!
-                        sbChar(cc, arg);
+                        parseCharClassSbChar(cc, arg); // goto sb_char /* [0-9-a] is allowed as [0-9\-a] */
                         break;
                     }
                     newSyntaxException(ERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS);
                 }
                 break;
-                
+
             case CC_CC_OPEN: /* [ */
                 CClassNode acc = parseCharClass();
                 cc.or(acc, enc);
                 break;
-                
+
             case CC_AND:     /* && */
                 if (arg.state == CCSTATE.VALUE) {
                     arg.v = 0; // ??? safe v ?
@@ -348,37 +334,37 @@ class Parser extends Lexer {
                     if (workCC == null) workCC = new CClassNode();
                     cc = workCC;
                 }
-                // initialize_cclass(cc); // clear it ??
-                break;              
-                
+                cc.clear();
+                break;
+
             case EOT:
                 newSyntaxException(ERR_PREMATURE_END_OF_CHAR_CLASS);
-                
-            default:                
-                newInternalException(ERR_PARSER_BUG);           
+
+            default:
+                newInternalException(ERR_PARSER_BUG);
             } // switch
-            
+
             if (!fetched) fetchTokenInCC();
-            
+
         } // while
-        
+
         if (arg.state == CCSTATE.VALUE) {
             arg.v = 0; // ??? safe v ?
             arg.vIsRaw = false;
             cc.nextStateValue(arg, env);
         }
-        
+
         if (prevCC != null) {
             prevCC.and(cc, enc);
             cc = prevCC;
         }
-        
+
         if (neg) {
             cc.setNot();
         } else {
             cc.clearNot();
         }
-        
+
         if (cc.isNot() && syntax.notNewlineInNegativeCC()) {
             if (!cc.isEmpty()) {
                 final int NEW_LINE = 0x0a;
@@ -391,81 +377,71 @@ class Parser extends Lexer {
                 }
             }
         }
-        
+
         return cc;
     }
-    
-    private void valEntry2(CClassNode cc, CCStateArg arg) {
-        cc.nextStateValue(arg, env);
-    }
-    
-    private void valEntry(CClassNode cc, CCStateArg arg) {
-        int len = enc.codeToMbcLength(arg.v);
-        arg.inType = len == 1 ? CCVALTYPE.SB : CCVALTYPE.CODE_POINT;
-        // !val_entry2:!
-        valEntry2(cc, arg);
-    }
-    
-    private void sbChar(CClassNode cc, CCStateArg arg) {
+
+    private void parseCharClassSbChar(CClassNode cc, CCStateArg arg) {
         arg.inType = CCVALTYPE.SB;
         arg.v = token.getC();
         arg.vIsRaw = false;
-        // !goto val_entry2;!
-        valEntry2(cc, arg);
+        parseCharClassValEntry2(cc, arg); // goto val_entry2
     }
 
-    private void rangeEndVal(CClassNode cc, CCStateArg arg) {
+    private void parseCharClassRangeEndVal(CClassNode cc, CCStateArg arg) {
         arg.v = '-';
         arg.vIsRaw = false;
-        // !goto val_entry;!
-        valEntry(cc, arg);
+        parseCharClassValEntry(cc, arg); // goto val_entry
+    }
+
+    private void parseCharClassValEntry(CClassNode cc, CCStateArg arg) {
+        int len = enc.codeToMbcLength(arg.v);
+        arg.inType = len == 1 ? CCVALTYPE.SB : CCVALTYPE.CODE_POINT;
+        parseCharClassValEntry2(cc, arg); // val_entry2:
+    }
+
+    private void parseCharClassValEntry2(CClassNode cc, CCStateArg arg) {
+        cc.nextStateValue(arg, env);
     }
-    
+
     private Node parseEnclose(TokenType term) {
         Node node = null;
-        
+
         if (!left()) newSyntaxException(ERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS);
-        
+
         int option = env.option;
-        
-        if (peekIs('?') && syntax.op2QMarkGroupEffect()) { 
+
+        if (peekIs('?') && syntax.op2QMarkGroupEffect()) {
             inc();
             if (!left()) newSyntaxException(ERR_END_PATTERN_IN_GROUP);
-            
+
             boolean listCapture = false;
-            
+
             fetch();
             switch(c) {
             case ':':  /* (?:...) grouping only */
-                // !group:!
-                fetchToken();
+                fetchToken(); // group:
                 node = parseSubExp(term);
                 returnCode = 1; /* group */
                 return node;
-                
             case '=':
                 node = new AnchorNode(AnchorType.PREC_READ);
                 break;
-                
             case '!':  /*         preceding read */
                 node = new AnchorNode(AnchorType.PREC_READ_NOT);
                 break;
-                
             case '>':  /* (?>...) stop backtrack */
                 node = new EncloseNode(EncloseType.STOP_BACKTRACK); // node_new_enclose
                 break;
-                
             case '\'':
                 if (Config.USE_NAMED_GROUP) {
                     if (syntax.op2QMarkLtNamedGroup()) {
-                        // !goto named_group1!;
-                        listCapture = false;
-                        node = namedGroup2(listCapture);
+                        listCapture = false; // goto named_group1
+                        node = parseEncloseNamedGroup2(listCapture);
                         break;
                     } else {
                         newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                     }
-                    break;
                 } // USE_NAMED_GROUP
                 break;
             case '<':  /* look behind (?<=...), (?<!...) */
@@ -479,32 +455,27 @@ class Parser extends Lexer {
                         if (syntax.op2QMarkLtNamedGroup()) {
                             unfetch();
                             c = '<';
-                            
-                            // !named_group1:!
-                            listCapture = false;
-                            // !named_group2:!
-                            node = namedGroup2(listCapture);
+
+                            listCapture = false; // named_group1:
+                            node = parseEncloseNamedGroup2(listCapture); // named_group2:
                             break;
                         } else {
                             newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                         }
-                        
+
                     } else { // USE_NAMED_GROUP
                         newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                     } // USE_NAMED_GROUP
                 }
                 break;
-                
             case '@':
-                if (syntax.op2AtMarkCaptureHistory()) {                 
+                if (syntax.op2AtMarkCaptureHistory()) {
                     if (Config.USE_NAMED_GROUP) {
-                        if (syntax.op2QMarkLtNamedGroup()) {                        
+                        if (syntax.op2QMarkLtNamedGroup()) {
                             fetch();
                             if (c == '<' || c == '\'') {
                                 listCapture = true;
-                                // /* (?@<name>...) */
-                                // goto !named_group2;!
-                                node = namedGroup2(listCapture);
+                                node = parseEncloseNamedGroup2(listCapture); // goto named_group2 /* (?@<name>...) */
                             }
                             unfetch();
                         }
@@ -518,7 +489,7 @@ class Parser extends Lexer {
                     newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                 }
                 break;
-                
+
             // case 'p': #ifdef USE_POSIXLINE_OPTION
             case '-':
             case 'i':
@@ -526,24 +497,20 @@ class Parser extends Lexer {
             case 's':
             case 'x':
                 boolean neg = false;
-                while(true) {
+                while (true) {
                     switch(c) {
                     case ':':
                     case ')':
                         break;
-                        
                     case '-':
                         neg = true;
                         break;
-                        
                     case 'x':
                         option = bsOnOff(option, Option.EXTEND, neg);
                         break;
-                        
                     case 'i':
                         option = bsOnOff(option, Option.IGNORECASE, neg);
                         break;
-                        
                     case 's':
                         if (syntax.op2OptionPerl()) {
                             option = bsOnOff(option, Option.MULTILINE, neg);
@@ -551,7 +518,6 @@ class Parser extends Lexer {
                             newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                         }
                         break;
-                        
                     case 'm':
                         if (syntax.op2OptionPerl()) {
                             option = bsOnOff(option, Option.SINGLELINE, !neg);
@@ -561,15 +527,14 @@ class Parser extends Lexer {
                             newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                         }
                         break;
-                        
                     // case 'p': #ifdef USE_POSIXLINE_OPTION // not defined
                     // option = bsOnOff(option, Option.MULTILINE|Option.SINGLELINE, neg);
                     // break;
-                    
+
                     default:
                         newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
                     } // switch
-                    
+
                     if (c == ')') {
                         EncloseNode en = new EncloseNode(option, 0); // node_new_option
                         node = en;
@@ -590,15 +555,14 @@ class Parser extends Lexer {
                     if (!left()) newSyntaxException(ERR_END_PATTERN_IN_GROUP);
                     fetch();
                 } // while
-            
+
             default:
                 newSyntaxException(ERR_UNDEFINED_GROUP_OPTION);
             } // switch
-            
+
         } else {
             if (isDontCaptureGroup(env.option)) {
-                // !goto group;!
-                fetchToken();
+                fetchToken(); // goto group
                 node = parseSubExp(term);
                 returnCode = 1; /* group */
                 return node;
@@ -608,7 +572,7 @@ class Parser extends Lexer {
             en.regNum = num;
             node = en;
         }
-        
+
         fetchToken();
         Node target = parseSubExp(term);
 
@@ -626,32 +590,31 @@ class Parser extends Lexer {
         returnCode = 0;
         return node; // ??
     }
-    
-    private Node namedGroup2(boolean listCapture) {
+
+    private Node parseEncloseNamedGroup2(boolean listCapture) {
         int nm = p;
         int num = fetchName(c, false);
         int nameEnd = value;
         num = env.addMemEntry();
         if (listCapture && num >= BitStatus.BIT_STATUS_BITS_NUM) newValueException(ERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY);
-        
+
         regex.nameAdd(bytes, nm, nameEnd, num, syntax);
         EncloseNode en = new EncloseNode(env.option, true); // node_new_enclose_memory
         en.regNum = num;
 
         Node node = en;
-        
+
         if (listCapture) env.captureHistory = bsOnAtSimple(env.captureHistory, num);
         env.numNamed++;
         return node;
     }
-    
-    private int nextChar; // hidden var
-    private int findStrPosition(int[]s, int n, int from, int to) {
+
+    private int findStrPosition(int[]s, int n, int from, int to, Ptr nextChar) {
         int x;
         int q;
         int p = from;
         int i = 0;
-        while(p < to) {
+        while (p < to) {
             x = enc.mbcToCode(bytes, p, to);
             q = p + enc.length(bytes, p, to);
             if (x == s[0]) {
@@ -661,7 +624,7 @@ class Parser extends Lexer {
                     q += enc.length(bytes, q, to);
                 }
                 if (i >= n) {
-                    if (bytes[nextChar] != 0) nextChar = q; // we may need zero term semantics... 
+                    if (bytes[nextChar.p] != 0) nextChar.p = q; // we may need zero term semantics...
                     return p;
                 }
             }
@@ -669,22 +632,18 @@ class Parser extends Lexer {
         }
         return -1;
     }
-    
+
     private Node parseExp(TokenType term) {
-        if (token.type == term) {
-            //!goto end_of_token;!
-            return new StringNode();
-        }
-        
+        if (token.type == term) return StringNode.EMPTY; // goto end_of_token
+
         Node node = null;
         boolean group = false;
 
         switch(token.type) {
         case ALT:
         case EOT:
-            // !end_of_token:!
-            return new StringNode(); // node_new_empty
-            
+            return StringNode.EMPTY; // end_of_token:, node_new_empty
+
         case SUBEXP_OPEN:
             node = parseEnclose(TokenType.SUBEXP_CLOSE);
             if (returnCode == 1) {
@@ -697,53 +656,55 @@ class Parser extends Lexer {
                 Node target = parseSubExp(term);
                 env.option = prev;
                 en.setTarget(target);
-                return node;                
+                return node;
             }
             break;
-            
         case SUBEXP_CLOSE:
             if (!syntax.allowUnmatchedCloseSubexp()) newSyntaxException(ERR_UNMATCHED_CLOSE_PARENTHESIS);
-            
             if (token.escaped) {
-                // !goto tk_raw_byte;!
-                return parseExpTkRawByte(group);
+                return parseExpTkRawByte(group); // goto tk_raw_byte
             } else {
-                // !goto tk_byte;!
-                return parseExpTkByte(group);
+                return parseExpTkByte(group); // goto tk_byte
             }
-            
         case STRING:
-            // !tk_byte:!
-            return parseExpTkByte(group);
-            
+            return parseExpTkByte(group); // tk_byte:
+
         case RAW_BYTE:
-            // !tk_raw_byte:!
-            return parseExpTkRawByte(group);
-            
+            return parseExpTkRawByte(group); // tk_raw_byte:
         case CODE_POINT:
             byte[]buf = new byte[Config.ENC_CODE_TO_MBC_MAXLEN];
             int num = enc.codeToMbc(token.getCode(), buf, 0);
-            // #ifdef NUMBERED_CHAR_IS_NOT_CASE_AMBIG ... // setRaw() #else 
+            // #ifdef NUMBERED_CHAR_IS_NOT_CASE_AMBIG ... // setRaw() #else
             node = new StringNode(buf, 0, num);
             break;
-            
+
         case QUOTE_OPEN:
             int[]endOp = new int[]{syntax.metaCharTable.esc, 'E'};
             int qstart = p;
-            int qend = findStrPosition(endOp, endOp.length, qstart, stop); // will set nextChar!!!
-            if (qend == -1) {
-                nextChar = qend = stop;
-            }
+            Ptr nextChar = new Ptr();
+            int qend = findStrPosition(endOp, endOp.length, qstart, stop, nextChar);
+            if (qend == -1) nextChar.p = qend = stop;
             node = new StringNode(bytes, qstart, qend);
-            p = nextChar;
+            p = nextChar.p;
             break;
 
         case CHAR_TYPE:
             switch(token.getPropCType()) {
+            case CharacterType.D:
+            case CharacterType.S:
+            case CharacterType.W:
+                if (Config.NON_UNICODE_SDW) {
+                    CClassNode cc = new CClassNode();
+                    cc.addCType(token.getPropCType(), false, env, this);
+                    if (token.getPropNot()) cc.setNot();
+                    node = cc;
+                }
+                break;
+
             case CharacterType.WORD:
                 node = new CTypeNode(token.getPropCType(), token.getPropNot());
                 break;
-            
+
             case CharacterType.SPACE:
             case CharacterType.DIGIT:
             case CharacterType.XDIGIT:
@@ -753,41 +714,41 @@ class Parser extends Lexer {
                 if (token.getPropNot()) ccn.setNot();
                 node = ccn;
                 break;
-                
+
             default:
                 newInternalException(ERR_PARSER_BUG);
-                
+
             } // inner switch
             break;
-            
+
         case CHAR_PROPERTY:
             node = parseCharProperty();
             break;
-            
+
         case CC_CC_OPEN:
             CClassNode cc = parseCharClass();
             node = cc;
             if (isIgnoreCase(env.option)) {
                 ApplyCaseFoldArg arg = new ApplyCaseFoldArg(env, cc);
                 enc.applyAllCaseFold(env.caseFoldFlag, ApplyCaseFold.INSTANCE, arg);
-            
+
                 if (arg.altRoot != null) {
                     node = ConsAltNode.newAltNode(node, arg.altRoot);
                 }
             }
             break;
-            
+
         case ANYCHAR:
             node = new AnyCharNode();
             break;
-            
+
         case ANYCHAR_ANYTIME:
             node = new AnyCharNode();
             QuantifierNode qn = new QuantifierNode(0, QuantifierNode.REPEAT_INFINITE, false);
             qn.setTarget(node);
             node = qn;
             break;
-            
+
         case BACKREF:
             int[]backRefs = token.getBackrefNum() > 1 ? token.getBackrefRefs() : new int[]{token.getBackrefRef1()};
             node = new BackRefNode(token.getBackrefNum(),
@@ -796,9 +757,9 @@ class Parser extends Lexer {
                             token.getBackrefExistLevel(), // #ifdef USE_BACKREF_AT_LEVEL
                             token.getBackrefLevel(),      // ...
                             env);
-            
+
             break;
-            
+
         case CALL:
             if (Config.USE_SUBEXP_CALL) {
                 int gNum = token.getCallGNum();
@@ -809,109 +770,101 @@ class Parser extends Lexer {
                 }
                 node = new CallNode(bytes, token.getCallNameP(), token.getCallNameEnd(), gNum);
                 env.numCall++;
-                break;
             } // USE_SUBEXP_CALL
             break;
 
         case ANCHOR:
             node = new AnchorNode(token.getAnchor()); // possible bug in oniguruma
             break;
-            
+
         case OP_REPEAT:
         case INTERVAL:
             if (syntax.contextIndepRepeatOps()) {
                 if (syntax.contextInvalidRepeatOps()) {
                     newSyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED);
                 } else {
-                    node = new StringNode(); // node_new_empty
+                    node = StringNode.EMPTY; // node_new_empty
                 }
             } else {
-                // !goto tk_byte;!
-                return parseExpTkByte(group);
+                return parseExpTkByte(group); // goto tk_byte
             }
             break;
-            
+
         default:
             newInternalException(ERR_PARSER_BUG);
         } //switch
-        
+
         //targetp = node;
-        
-        // !re_entry:!
-        fetchToken();
-        
-        // !repeat:!
-        return parseExpRepeat(node, group);
+
+        fetchToken(); // re_entry:
+
+        return parseExpRepeat(node, group); // repeat:
     }
-    
+
     private Node parseExpTkByte(boolean group) {
-        // !tk_byte:!
-        StringNode node = new StringNode(bytes, token.backP, p);
+        StringNode node = new StringNode(bytes, token.backP, p); // tk_byte:
         while (true) {
             fetchToken();
             if (token.type != TokenType.STRING) break;
-            
+
             if (token.backP == node.end) {
                 node.end = p; // non escaped character, remain shared, just increase shared range
             } else {
-                node.cat(bytes, token.backP, p); // non continuous string stream, need to COW 
+                node.cat(bytes, token.backP, p); // non continuous string stream, need to COW
             }
-        }       
-        // !string_end:!
+        }
         // targetp = node;
-        // !goto repeat;!
-        return parseExpRepeat(node, group);
+        return parseExpRepeat(node, group); // string_end:, goto repeat
     }
-    
+
     private Node parseExpTkRawByte(boolean group) {
-        // !tk_raw_byte:!
+        // tk_raw_byte:
 
         // important: we don't use 0xff mask here neither in the compiler
         // (in the template string) so we won't have to mask target
-        // strings when comparing against them in the matcher 
+        // strings when comparing against them in the matcher
         StringNode node = new StringNode((byte)token.getC());
         node.setRaw();
 
-        int len = 1;            
+        int len = 1;
         while (true) {
-            if (len >= enc.minLength()) {               
-                if (len == enc.length(node.bytes, node.p, node.end)) {                  
+            if (len >= enc.minLength()) {
+                if (len == enc.length(node.bytes, node.p, node.end)) {
                     fetchToken();
                     node.clearRaw();
                     // !goto string_end;!
                     return parseExpRepeat(node, group);
                 }
             }
-            
+
             fetchToken();
             if (token.type != TokenType.RAW_BYTE) {
                 /* Don't use this, it is wrong for little endian encodings. */
                 // USE_PAD_TO_SHORT_BYTE_CHAR ...
-                
+
                 newValueException(ERR_TOO_SHORT_MULTI_BYTE_STRING);
             }
 
             // important: we don't use 0xff mask here neither in the compiler
             // (in the template string) so we won't have to mask target
-            // strings when comparing against them in the matcher 
+            // strings when comparing against them in the matcher
             node.cat((byte)token.getC());
             len++;
         } // while
     }
-    
+
     private Node parseExpRepeat(Node target, boolean group) {
-        // !repeat:!
-        while (token.type == TokenType.OP_REPEAT || token.type == TokenType.INTERVAL) {
+        while (token.type == TokenType.OP_REPEAT || token.type == TokenType.INTERVAL) { // repeat:
             if (target.isInvalidQuantifier()) newSyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_INVALID);
 
             QuantifierNode qtfr = new QuantifierNode(token.getRepeatLower(),
                                                      token.getRepeatUpper(),
                                                      token.type == TokenType.INTERVAL);
-            
+
             qtfr.greedy = token.getRepeatGreedy();
             int ret = qtfr.setQuantifier(target, group, env, bytes, getBegin(), getEnd());
             Node qn = qtfr;
-            
+
             if (token.getRepeatPossessive()) {
                 EncloseNode en = new EncloseNode(EncloseType.STOP_BACKTRACK); // node_new_enclose
                 en.setTarget(qn);
@@ -923,29 +876,27 @@ class Parser extends Lexer {
             } else if (ret == 2) { /* split case: /abc+/ */
                 target = ConsAltNode.newListNode(target, null);
                 ConsAltNode tmp = ((ConsAltNode)target).setCdr(ConsAltNode.newListNode(qn, null));
-                
+
                 fetchToken();
                 return parseExpRepeatForCar(target, tmp, group);
             }
-            // !goto re_entry;!
-            fetchToken();
+            fetchToken(); // goto re_entry
         }
         return target;
     }
 
     private Node parseExpRepeatForCar(Node top, ConsAltNode target, boolean group) {
-        // !repeat:!
-        while (token.type == TokenType.OP_REPEAT || token.type == TokenType.INTERVAL) {
+        while (token.type == TokenType.OP_REPEAT || token.type == TokenType.INTERVAL) { // repeat:
             if (target.car.isInvalidQuantifier()) newSyntaxException(ERR_TARGET_OF_REPEAT_OPERATOR_INVALID);
 
             QuantifierNode qtfr = new QuantifierNode(token.getRepeatLower(),
                                                      token.getRepeatUpper(),
                                                      token.type == TokenType.INTERVAL);
-            
+
             qtfr.greedy = token.getRepeatGreedy();
             int ret = qtfr.setQuantifier(target.car, group, env, bytes, getBegin(), getEnd());
             Node qn = qtfr;
-            
+
             if (token.getRepeatPossessive()) {
                 EncloseNode en = new EncloseNode(EncloseType.STOP_BACKTRACK); // node_new_enclose
                 en.setTarget(qn);
@@ -957,11 +908,10 @@ class Parser extends Lexer {
             } else if (ret == 2) { /* split case: /abc+/ */
                 assert false;
             }
-            // !goto re_entry;!
-            fetchToken();
+            fetchToken(); // goto re_entry
         }
         return top;
-    }   
+    }
 
     private Node parseBranch(TokenType term) {
         Node node = parseExp(term);
@@ -971,13 +921,13 @@ class Parser extends Lexer {
         } else {
             ConsAltNode top = ConsAltNode.newListNode(node, null);
             ConsAltNode t = top;
-            
+
             while (token.type != TokenType.EOT && token.type != term && token.type != TokenType.ALT) {
                 node = parseExp(term);
                 if (node.getType() == NodeType.LIST) {
                     t.setCdr((ConsAltNode)node);
                     while (((ConsAltNode)node).cdr != null ) node = ((ConsAltNode)node).cdr;
-                    
+
                     t = ((ConsAltNode)node);
                 } else {
                     t.setCdr(ConsAltNode.newListNode(node, null));
@@ -987,7 +937,7 @@ class Parser extends Lexer {
             return top;
         }
     }
-    
+
     /* term_tok: TK_EOT or TK_SUBEXP_CLOSE */
     private Node parseSubExp(TokenType term) {
         Node node = parseBranch(term);
@@ -1000,11 +950,11 @@ class Parser extends Lexer {
             while (token.type == TokenType.ALT) {
                 fetchToken();
                 node = parseBranch(term);
-                
+
                 t.setCdr(ConsAltNode.newAltNode(node, null));
                 t = t.cdr;
             }
-            
+
             if (token.type != term) parseSubExpError(term);
             return top;
         } else {
@@ -1012,7 +962,7 @@ class Parser extends Lexer {
             return null; //not reached
         }
     }
-    
+
     private void parseSubExpError(TokenType term) {
         if (term == TokenType.SUBEXP_CLOSE) {
             newSyntaxException(ERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS);
@@ -1020,7 +970,7 @@ class Parser extends Lexer {
             newInternalException(ERR_PARSER_BUG);
         }
     }
-    
+
     private Node parseRegexp() {
         fetchToken();
         return parseSubExp(TokenType.EOT);
diff --git a/src/org/joni/Regex.java b/src/org/joni/Regex.java
index d6308de..03adf2b 100644
--- a/src/org/joni/Regex.java
+++ b/src/org/joni/Regex.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -27,6 +27,9 @@ import java.util.IllegalFormatConversionException;
 import java.util.Iterator;
 
 import org.jcodings.Encoding;
+import org.jcodings.EncodingDB;
+import org.jcodings.specific.ASCIIEncoding;
+import org.jcodings.specific.UTF8Encoding;
 import org.jcodings.util.BytesHash;
 import org.joni.constants.AnchorType;
 import org.joni.constants.RegexState;
@@ -35,7 +38,7 @@ import org.joni.exception.InternalException;
 import org.joni.exception.ValueException;
 
 public final class Regex implements RegexState {
-    
+
     int[] code;             /* compiled pattern */
     int codeLength;
     boolean stackNeeded;
@@ -66,9 +69,9 @@ public final class Regex implements RegexState {
     Object userObject;
     //final Syntax syntax;
     final int caseFoldFlag;
-    
+
     BytesHash<NameEntry> nameTable;        // named entries
-    
+
     /* optimization info (string search, char-map and anchors) */
     SearchAlgorithm searchAlgorithm;        /* optimize flag */
     int thresholdLength;                    /* search str-length for apply optimize */
@@ -76,21 +79,52 @@ public final class Regex implements RegexState {
     int anchorDmin;                         /* (SEMI_)END_BUF anchor distance */
     int anchorDmax;                         /* (SEMI_)END_BUF anchor distance */
     int subAnchor;                          /* start-anchor for exact or map */
-    
+
     byte[]exact;
     int exactP;
     int exactEnd;
-    
+
     byte[]map;                              /* used as BM skip or char-map */
     int[]intMap;                            /* BM skip for exact_len > 255 */
     int[]intMapBackward;                    /* BM skip for backward search */
     int dMin;                               /* min-distance of exact or map */
     int dMax;                               /* max-distance of exact or map */
 
+    byte[][]templates;
+    int templateNum;
+
+    public Regex(CharSequence cs) {
+        this(cs.toString());
+    }
+
+    public Regex(CharSequence cs, Encoding enc) {
+        this(cs.toString(), enc);
+    }
+
+    public Regex(String str) {
+        this(str.getBytes(), 0, str.length(), 0, UTF8Encoding.INSTANCE);
+    }
+
+    public Regex(String str, Encoding enc) {
+        this(str.getBytes(), 0, str.length(), 0, enc);
+    }
+
+    public Regex(byte[] bytes) {
+        this(bytes, 0, bytes.length, 0, ASCIIEncoding.INSTANCE);
+    }
+
+    public Regex(byte[] bytes, int p, int end) {
+        this(bytes, p, end, 0, ASCIIEncoding.INSTANCE);
+    }
+
+    public Regex(byte[] bytes, int p, int end, int option) {
+        this(bytes, p, end, option, ASCIIEncoding.INSTANCE);
+    }
+
     public Regex(byte[]bytes, int p, int end, int option, Encoding enc) {
         this(bytes, p, end, option, enc, Syntax.RUBY, WarnCallback.DEFAULT);
     }
-    
+
     // onig_new
     public Regex(byte[]bytes, int p, int end, int option, Encoding enc, Syntax syntax) {
         this(bytes, p, end, option, Config.ENC_CASE_FOLD_DEFAULT, enc, syntax, WarnCallback.DEFAULT);
@@ -99,7 +133,7 @@ public final class Regex implements RegexState {
     public Regex(byte[]bytes, int p, int end, int option, Encoding enc, WarnCallback warnings) {
         this(bytes, p, end, option, enc, Syntax.RUBY, warnings);
     }
-    
+
     // onig_new
     public Regex(byte[]bytes, int p, int end, int option, Encoding enc, Syntax syntax, WarnCallback warnings) {
         this(bytes, p, end, option, Config.ENC_CASE_FOLD_DEFAULT, enc, syntax, warnings);
@@ -107,19 +141,19 @@ public final class Regex implements RegexState {
 
     // onig_alloc_init
     public Regex(byte[]bytes, int p, int end, int option, int caseFoldFlag, Encoding enc, Syntax syntax, WarnCallback warnings) {
-        
+
         if ((option & (Option.DONT_CAPTURE_GROUP | Option.CAPTURE_GROUP)) ==
             (Option.DONT_CAPTURE_GROUP | Option.CAPTURE_GROUP)) {
             throw new ValueException(ErrorMessages.ERR_INVALID_COMBINATION_OF_OPTIONS);
         }
-        
+
         if ((option & Option.NEGATE_SINGLELINE) != 0) {
             option |= syntax.options;
             option &= ~Option.SINGLELINE;
         } else {
             option |= syntax.options;
         }
-        
+
         this.enc = enc;
         this.options = option;
         this.caseFoldFlag = caseFoldFlag;
@@ -129,19 +163,19 @@ public final class Regex implements RegexState {
 
         this.warnings = null;
     }
-    
+
     public Matcher matcher(byte[]bytes) {
         return matcher(bytes, 0, bytes.length);
     }
-    
+
     public Matcher matcher(byte[]bytes, int p, int end) {
         return factory.create(this, bytes, p, end);
     }
-    
+
     public int numberOfCaptures() {
         return numMem;
     }
-    
+
     public int numberOfCaptureHistories() {
         if (Config.USE_CAPTURE_HISTORY) {
             int n = 0;
@@ -153,10 +187,10 @@ public final class Regex implements RegexState {
             return 0;
         }
     }
-    
+
     String nameTableToString() {
         StringBuilder sb = new StringBuilder();
-        
+
         if (nameTable != null) {
             sb.append("name table\n");
             for (NameEntry ne : nameTable) {
@@ -166,7 +200,7 @@ public final class Regex implements RegexState {
         }
         return sb.toString();
     }
-    
+
     NameEntry nameFind(byte[]name, int nameP, int nameEnd) {
         if (nameTable != null) return nameTable.get(name, nameP, nameEnd);
         return null;
@@ -184,7 +218,7 @@ public final class Regex implements RegexState {
                 }
             }
         }
-    }    
+    }
 
     public int numberOfNames() {
         return nameTable == null ? 0 : nameTable.size();
@@ -199,7 +233,7 @@ public final class Regex implements RegexState {
         } else {
             e = nameFind(name, nameP, nameEnd);
         }
-        
+
         if (e == null) {
             // dup the name here as oni does ?, what for ? (it has to manage it, we don't)
             e = new NameEntry(name, nameP, nameEnd);
@@ -230,7 +264,7 @@ public final class Regex implements RegexState {
                 for (int i = e.backNum - 1; i >= 0; i--) {
                     if (region.beg[e.backRefs[i]] != Region.REGION_NOTPOS) return e.backRefs[i];
                 }
-            } 
+            }
             return e.backRefs[e.backNum - 1];
         }
     }
@@ -241,7 +275,7 @@ public final class Regex implements RegexState {
 
     public boolean noNameGroupIsActive(Syntax syntax) {
         if (isDontCaptureGroup(options)) return false;
-        
+
         if (Config.USE_NAMED_GROUP) {
             if (numberOfNames() > 0 && syntax.captureOnlyNamedGroup() && !isCaptureGroup(options)) return false;
         }
@@ -263,7 +297,7 @@ public final class Regex implements RegexState {
             for (int i=0; i<len-1; i++) map[bytes[p + i] & 0xff] = (byte)(len - 1 -i); // oxff ??
         } else {
             if (intMap == null) intMap = new int[Config.CHAR_TABLE_SIZE];
-            
+
             for (int i=0; i<len-1; i++) intMap[bytes[p + i] & 0xff] = len - 1 - i; // oxff ??
         }
     }
@@ -272,7 +306,7 @@ public final class Regex implements RegexState {
         if (e.length == 0) return;
 
         // shall we copy that ?
-        exact = e.s;
+        exact = e.bytes;
         exactP = 0;
         exactEnd = e.length;
 
@@ -281,44 +315,39 @@ public final class Regex implements RegexState {
             searchAlgorithm = enc.toLowerCaseTable() != null ? SearchAlgorithm.SLOW_IC_SB : new SearchAlgorithm.SLOW_IC(this);
         } else {
             boolean allowReverse = enc.isReverseMatchAllowed(exact, exactP, exactEnd);
-            
+
             if (e.length >= 3 || (e.length >= 2 && allowReverse)) {
                 setupBMSkipMap();
                 if (allowReverse) {
                     searchAlgorithm = SearchAlgorithm.BM;
                 } else {
-                    searchAlgorithm = SearchAlgorithm.BM_NOT_REV;                    
+                    searchAlgorithm = SearchAlgorithm.BM_NOT_REV;
                 }
             } else {
                 searchAlgorithm = enc.isSingleByte() ? SearchAlgorithm.SLOW_SB : SearchAlgorithm.SLOW;
             }
         }
-        
+
         dMin = e.mmd.min;
         dMax = e.mmd.max;
-        
+
         if (dMin != MinMaxLen.INFINITE_DISTANCE) {
             thresholdLength = dMin + (exactEnd - exactP);
         }
     }
 
     void setOptimizeMapInfo(OptMapInfo m) {
-        /*
-        for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
-            map[i] = m.map[i]; // do we really have to copy that ???
-        }
-        */
         map = m.map;
-        
+
         searchAlgorithm = enc.isSingleByte() ? SearchAlgorithm.MAP_SB : SearchAlgorithm.MAP;
         dMin = m.mmd.min;
         dMax = m.mmd.max;
-        
+
         if (dMin != MinMaxLen.INFINITE_DISTANCE) {
             thresholdLength = dMin + 1;
         }
     }
-    
+
     void setSubAnchor(OptAnchorInfo anc) {
         subAnchor |= anc.leftAnchor & AnchorType.BEGIN_LINE;
         subAnchor |= anc.rightAnchor & AnchorType.END_LINE;
@@ -330,14 +359,14 @@ public final class Regex implements RegexState {
         anchorDmax = 0;
         anchorDmin = 0;
         subAnchor = 0;
-        
+
         exact = null;
         exactP = exactEnd = 0;
     }
-    
+
     public String encStringToString(byte[]bytes, int p, int end) {
         StringBuilder sb = new StringBuilder("\nPATTERN: /");
-        
+
         if (enc.minLength() > 1) {
             int p_ = p;
             while (p_ < end) {
@@ -361,12 +390,12 @@ public final class Regex implements RegexState {
         }
         return sb.append("/").toString();
     }
-    
+
     public String optimizeInfoToString() {
         String s = "";
         s += "optimize: " + searchAlgorithm.getName() + "\n";
         s += "  anchor:     " + OptAnchorInfo.anchorToString(anchor);
-        
+
         if ((anchor & AnchorType.END_BUF_MASK) != 0) {
             s += MinMaxLen.distanceRangeToString(anchorDmin, anchorDmax);
         }
@@ -381,11 +410,11 @@ public final class Regex implements RegexState {
         s += "threshold length: " + thresholdLength + "\n";
 
         if (exact != null) {
-            s += "exact: [" + new String(exact, exactP, exactEnd - exactP) + "]: length: " + (exactEnd - exactP) + "\n"; 
+            s += "exact: [" + new String(exact, exactP, exactEnd - exactP) + "]: length: " + (exactEnd - exactP) + "\n";
         } else if (searchAlgorithm == SearchAlgorithm.MAP || searchAlgorithm == SearchAlgorithm.MAP_SB) {
             int n=0;
             for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) if (map[i] != 0) n++;
-            
+
             s += "map: n = " + n + "\n";
             if (n > 0) {
                 int c=0;
@@ -401,6 +430,7 @@ public final class Regex implements RegexState {
                 s += "]\n";
             }
         }
+
         return s;
     }
 
diff --git a/src/org/joni/Region.java b/src/org/joni/Region.java
index 08b90f9..cb54315 100644
--- a/src/org/joni/Region.java
+++ b/src/org/joni/Region.java
@@ -1,44 +1,44 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
 
 public final class Region {
     static final int REGION_NOTPOS = -1;
-    
+
     public final int numRegs;
     public final int[]beg;
     public final int[]end;
     public CaptureTreeNode historyRoot;
-    
+
     public Region(int num) {
         this.numRegs = num;
         this.beg = new int[num];
         this.end = new int[num];
     }
-    
+
     public Region(int begin, int end) {
         this.numRegs = 1;
         this.beg = new int[]{begin};
         this.end = new int[]{end};
     }
-    
+
     public Region clone() {
         Region region = new Region(numRegs);
         System.arraycopy(beg, 0, region.beg, 0, beg.length);
@@ -46,18 +46,18 @@ public final class Region {
         if (historyRoot != null) region.historyRoot = historyRoot.cloneTree();
         return region;
     }
-    
+
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("Region: \n");
         for (int i=0; i<beg.length; i++) sb.append(" " + i + ": (" + beg[i] + "-" + end[i] + ")");
         return sb.toString();
     }
-    
+
     CaptureTreeNode getCaptureTree() {
         return historyRoot;
     }
-    
+
     void clear() {
         for (int i=0; i<beg.length; i++) {
             beg[i] = end[i] = REGION_NOTPOS;
diff --git a/src/org/joni/ScanEnvironment.java b/src/org/joni/ScanEnvironment.java
index 95dd90a..02a1ad7 100644
--- a/src/org/joni/ScanEnvironment.java
+++ b/src/org/joni/ScanEnvironment.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -27,9 +27,9 @@ import org.joni.exception.ErrorMessages;
 import org.joni.exception.InternalException;
 
 public final class ScanEnvironment {
-    
+
     private static final int SCANENV_MEMNODES_SIZE = 8;
-    
+
     int option;
     final int caseFoldFlag;
     final public Encoding enc;
@@ -38,23 +38,23 @@ public final class ScanEnvironment {
     int btMemStart;
     int btMemEnd;
     int backrefedMem;
-    
+
     final public Regex reg;
-    
+
     int numCall;
     UnsetAddrList unsetAddrList; // USE_SUBEXP_CALL
     public int numMem;
 
     int numNamed; // USE_NAMED_GROUP
-    
+
     public Node memNodes[];
-    
+
     // USE_COMBINATION_EXPLOSION_CHECK
     int numCombExpCheck;
     int combExpMaxRegNum;
     int currMaxRegNum;
     boolean hasRecursion;
-    
+
     public ScanEnvironment(Regex regex, Syntax syntax) {
         this.reg = regex;
         option = regex.options;
@@ -81,7 +81,7 @@ public final class ScanEnvironment {
         currMaxRegNum = 0;
         hasRecursion = false;
     }
-    
+
     public int addMemEntry() {
         if (numMem++ == 0) {
             memNodes = new Node[SCANENV_MEMNODES_SIZE];
@@ -93,7 +93,7 @@ public final class ScanEnvironment {
 
         return numMem;
     }
-    
+
     public void setMemNode(int num, Node node) {
         if (numMem >= num) {
             memNodes[num] = node;
@@ -101,7 +101,7 @@ public final class ScanEnvironment {
             throw new InternalException(ErrorMessages.ERR_PARSER_BUG);
         }
     }
-    
+
     public int convertBackslashValue(int c) {
         if (syntax.opEscControlChars()) {
             switch (c) {
@@ -121,7 +121,7 @@ public final class ScanEnvironment {
         }
         return c;
     }
-    
+
     void ccEscWarn(String s) {
         if (Config.USE_WARN) {
             if (syntax.warnCCOpNotEscaped() && syntax.backSlashEscapeInCC()) {
@@ -129,7 +129,7 @@ public final class ScanEnvironment {
             }
         }
     }
-    
+
     void closeBracketWithoutEscapeWarn(String s) {
         if (Config.USE_WARN) {
             if (syntax.warnCCOpNotEscaped()) {
diff --git a/src/org/joni/ScannerSupport.java b/src/org/joni/ScannerSupport.java
index 370d338..8598fc6 100644
--- a/src/org/joni/ScannerSupport.java
+++ b/src/org/joni/ScannerSupport.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -28,7 +28,7 @@ import org.joni.exception.ValueException;
 
 abstract class ScannerSupport extends IntHolder implements ErrorMessages {
     protected final Encoding enc;       // fast access to encoding
-    
+
     protected final byte[]bytes;        // pattern
     protected int p;                    // current scanner position
     protected int stop;                 // pattern end (mutable)
@@ -38,28 +38,29 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
     private final int begin;            // pattern begin position for reset() support
     private final int end;              // pattern end position for reset() support
     protected int _p;                   // used by mark()/restore() to mark positions
-    
+
     protected ScannerSupport(Encoding enc, byte[]bytes, int p, int end) {
         this.enc = enc;
-        
+
         this.bytes = bytes;
         this.begin = p;
         this.end = end;
-        
+
         reset();
     }
-    
+
     protected int getBegin() {
         return begin;
     }
-    
+
     protected int getEnd() {
         return end;
     }
-    
+
     private final int INT_SIGN_BIT = 1 << 31;
-    
+
     protected final int scanUnsignedNumber() {
+        int last = c;
         int num = 0; // long ???
         while(left()) {
             fetch();
@@ -68,14 +69,16 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
                 num = num * 10 + Encoding.digitVal(c);
                 if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
             } else {
-                unfetch();              
+                unfetch();
                 break;
             }
-        }       
-        return num;     
+        }
+        c = last;
+        return num;
     }
-    
+
     protected final int scanUnsignedHexadecimalNumber(int maxLength) {
+        int last = c;
         int num = 0;
         while(left() && maxLength-- != 0) {
             fetch();
@@ -89,15 +92,17 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
                 break;
             }
         }
+        c = last;
         return num;
     }
-    
+
     protected final int scanUnsignedOctalNumber(int maxLength) {
+        int last = c;
         int num = 0;
         while(left() && maxLength-- != 0) {
             fetch();
-            if (enc.isDigit(c) && c < '8') {                
-                int onum = num;             
+            if (enc.isDigit(c) && c < '8') {
+                int onum = num;
                 int val = Encoding.odigitVal(c);
                 num = (num << 3) + val;
                 if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
@@ -106,56 +111,57 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
                 break;
             }
         }
-        return num;     
+        c = last;
+        return num;
     }
-    
+
     protected final void reset() {
         p = begin;
         stop = end;
     }
-    
+
     protected final void mark() {
         _p = p;
     }
-    
+
     protected final void restore() {
         p = _p;
     }
-    
+
     protected final void inc() {
         lastFetched = p;
         p += enc.length(bytes, p, stop);
     }
-    
+
     protected final void fetch() {
         c = enc.mbcToCode(bytes, p, stop);
         lastFetched = p;
         p += enc.length(bytes, p, stop);
     }
-    
+
     protected int fetchTo() {
         int to = enc.mbcToCode(bytes, p, stop);
         lastFetched = p;
         p += enc.length(bytes, p, stop);
         return to;
     }
-    
+
     protected final void unfetch() {
         p = lastFetched;
     }
-    
+
     protected final int peek() {
-        return p < stop ? enc.mbcToCode(bytes, p, stop) : 0; 
+        return p < stop ? enc.mbcToCode(bytes, p, stop) : 0;
     }
-    
+
     protected final boolean peekIs(int c) {
         return peek() == c;
     }
-    
+
     protected final boolean left() {
         return p < stop;
     }
-    
+
     protected void newSyntaxException(String message) {
         throw new SyntaxException(message);
     }
@@ -163,7 +169,7 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
     protected void newValueException(String message) {
         throw new ValueException(message);
     }
-    
+
     protected void newValueException(String message, String str) {
         throw new ValueException(message, str);
     }
@@ -175,5 +181,5 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
     protected void newInternalException(String message) {
         throw new InternalException(message);
     }
-    
+
 }
diff --git a/src/org/joni/SearchAlgorithm.java b/src/org/joni/SearchAlgorithm.java
index 36bf877..6d63c2f 100644
--- a/src/org/joni/SearchAlgorithm.java
+++ b/src/org/joni/SearchAlgorithm.java
@@ -1,3 +1,22 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
 package org.joni;
 
 import org.jcodings.Encoding;
@@ -8,44 +27,44 @@ public abstract class SearchAlgorithm {
     public abstract String getName();
     public abstract int search(Regex regex, byte[]text, int textP, int textEnd, int textRange);
     public abstract int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_);
-    
+
 
     public static final SearchAlgorithm NONE = new SearchAlgorithm() {
 
         public final String getName() {
             return "NONE";
         }
-        
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             return textP;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             return textP;
         }
-        
+
     };
-    
+
     public static final SearchAlgorithm SLOW = new SearchAlgorithm() {
-        
+
         public final String getName() {
             return "EXACT";
         }
-        
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             Encoding enc = regex.enc;
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
-            
+
+
             int end = textEnd;
             end -= targetEnd - targetP - 1;
-            
+
             if (end > textRange) end = textRange;
-            
+
             int s = textP;
-            
+
             while (s < end) {
                 if (text[s] == target[targetP]) {
                     int p = s + 1;
@@ -54,30 +73,30 @@ public abstract class SearchAlgorithm {
                         if (target[t] != text[p++]) break;
                         t++;
                     }
-                    
+
                     if (t == targetEnd) return s;
                 }
                 s += enc.length(text, s, textEnd);
             }
-            
-            return -1;            
+
+            return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             Encoding enc = regex.enc;
             byte[]target = regex.exact;
             int targetP = regex.exactP;
-            int targetEnd = regex.exactEnd;            
-            
+            int targetEnd = regex.exactEnd;
+
             int s = textEnd;
             s -= targetEnd - targetP;
-            
+
             if (s > textStart) {
                 s = textStart;
             } else {
                 s = enc.leftAdjustCharHead(text, adjustText, s, textEnd);
             }
-            
+
             while (s >= textP) {
                 if (text[s] == target[targetP]) {
                     int p = s + 1;
@@ -90,28 +109,28 @@ public abstract class SearchAlgorithm {
                 }
                 s = enc.prevCharHead(text, adjustText, s, textEnd);
             }
-            return -1;            
+            return -1;
         }
     };
 
     public static final SearchAlgorithm SLOW_SB = new SearchAlgorithm() {
-        
+
         public final String getName() {
             return "EXACT_SB";
         }
-        
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
+
             int end = textEnd;
             end -= targetEnd - targetP - 1;
-            
+
             if (end > textRange) end = textRange;
-            
+
             int s = textP;
-            
+
             while (s < end) {
                 if (text[s] == target[targetP]) {
                     int p = s + 1;
@@ -120,25 +139,25 @@ public abstract class SearchAlgorithm {
                         if (target[t] != text[p++]) break;
                         t++;
                     }
-                    
+
                     if (t == targetEnd) return s;
                 }
                 s++;
             }
-            
-            return -1;            
+
+            return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             byte[]target = regex.exact;
             int targetP = regex.exactP;
-            int targetEnd = regex.exactEnd;            
-            
+            int targetEnd = regex.exactEnd;
+
             int s = textEnd;
             s -= targetEnd - targetP;
-            
+
             if (s > textStart) s = textStart;
-            
+
             while (s >= textP) {
                 if (text[s] == target[targetP]) {
                     int p = s + 1;
@@ -152,11 +171,11 @@ public abstract class SearchAlgorithm {
                 //s = s <= adjustText ? -1 : s - 1;
                 s--;
             }
-            return -1;            
+            return -1;
         }
     };
-    
-    
+
+
     public static final class SLOW_IC extends SearchAlgorithm {
         private final byte[]buf = new byte[Config.ENC_MBC_CASE_FOLD_MAXLEN];
         private final IntHolder holder = new IntHolder();
@@ -167,50 +186,50 @@ public abstract class SearchAlgorithm {
             this.caseFoldFlag = regex.caseFoldFlag;
             this.enc = regex.enc;
         }
-        
+
         public final String getName() {
             return "EXACT_IC";
-        }        
-        
+        }
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
+
             int end = textEnd;
             end -= targetEnd - targetP - 1;
-            
+
             if (end > textRange) end = textRange;
             int s = textP;
-            
+
             while (s < end) {
                 if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) return s;
                 s += enc.length(text, s, textEnd);
             }
             return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
+
             int s = textEnd;
             s -= targetEnd - targetP;
-            
+
             if (s > textStart) {
                 s = textStart;
             } else {
                 s = enc.leftAdjustCharHead(text, adjustText, s, textEnd);
             }
-            
+
             while (s >= textP) {
                 if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) return s;
                 s = enc.prevCharHead(text, adjustText, s, textEnd);
             }
-            return -1;            
+            return -1;
         }
-        
+
         private boolean lowerCaseMatch(byte[]t, int tP, int tEnd,
                                        byte[]bytes, int p, int end) {
 
@@ -222,7 +241,7 @@ public abstract class SearchAlgorithm {
                 } else {
                     int q = 0;
                     while (lowlen > 0) {
-                        if (t[tP++] != buf[q++]) return false; 
+                        if (t[tP++] != buf[q++]) return false;
                         lowlen--;
                     }
                 }
@@ -235,20 +254,20 @@ public abstract class SearchAlgorithm {
 
         public final String getName() {
             return "EXACT_IC_SB";
-        }        
-        
-        public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {            
+        }
+
+        public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             final byte[]toLowerTable = regex.enc.toLowerCaseTable();
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
+
             int end = textEnd;
             end -= targetEnd - targetP - 1;
-            
+
             if (end > textRange) end = textRange;
             int s = textP;
-            
+
             while (s < end) {
                 if (target[targetP] == toLowerTable[text[s] & 0xff]) {
                     int p = s + 1;
@@ -257,25 +276,25 @@ public abstract class SearchAlgorithm {
                         if (target[t] != toLowerTable[text[p++] & 0xff]) break;
                         t++;
                     }
-                    
+
                     if (t == targetEnd) return s;
                 }
-                s++;                
+                s++;
             }
             return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             final byte[]toLowerTable = regex.enc.toLowerCaseTable();
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
+
             int s = textEnd;
             s -= targetEnd - targetP;
-            
+
             if (s > textStart) s = textStart;
-            
+
             while (s >= textP) {
                 if (target[targetP] == toLowerTable[text[s] & 0xff]) {
                     int p = s + 1;
@@ -289,76 +308,80 @@ public abstract class SearchAlgorithm {
                 //s = s <= adjustText ? -1 : s - 1;
                 s--;
             }
-            return -1;            
+            return -1;
         }
 
-    };    
-    
+    };
+
     public static final SearchAlgorithm BM = new SearchAlgorithm() {
-        
+
         public final String getName() {
             return "EXACT_BM";
-        }        
-        
+        }
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             byte[]target = regex.exact;
             int targetP = regex.exactP;
-            int targetEnd = regex.exactEnd;            
-            
+            int targetEnd = regex.exactEnd;
+
             int end = textRange + (targetEnd - targetP) - 1;
             if (end > textEnd) end = textEnd;
-            
+
             int tail = targetEnd - 1;
             int s = textP + (targetEnd - targetP) - 1;
-            
+
             if (regex.intMap == null) {
                 while (s < end) {
                     int p = s;
                     int t = tail;
-                    while (t >= targetP && text[p] == target[t]) {
+
+                    while (text[p] == target[t]) {
+                        if (t == targetP) return p;
                         p--; t--;
                     }
-                    if (t < targetP) return p + 1;
+
                     s += regex.map[text[s] & 0xff];
                 }
             } else { /* see int_map[] */
                 while (s < end) {
                     int p = s;
                     int t = tail;
-                    while (t >= targetP && text[p] == target[t]) {
+
+                    while (text[p] == target[t]) {
+                        if (t == targetP) return p;
                         p--; t--;
                     }
-                    if (t < targetP) return p + 1;
+
                     s += regex.intMap[text[s] & 0xff];
                 }
             }
-            return -1;            
+            return -1;
         }
-        
+
         private static final int BM_BACKWARD_SEARCH_LENGTH_THRESHOLD = 100;
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             Encoding enc = regex.enc;
             byte[]target = regex.exact;
             int targetP = regex.exactP;
             int targetEnd = regex.exactEnd;
-            
+
             if (regex.intMapBackward == null) {
                 if (s_ - range_ < BM_BACKWARD_SEARCH_LENGTH_THRESHOLD) {
                     // goto exact_method;
                     return SLOW.searchBackward(regex, text, textP, adjustText, textEnd, textStart, s_, range_);
                 }
-                setBmBackwardSkip(regex, target, targetP, targetEnd);                    
+                setBmBackwardSkip(regex, target, targetP, targetEnd);
             }
-            
+
             int s = textEnd - (targetEnd - targetP);
-            
+
             if (textStart < s) {
                 s = textStart;
             } else {
                 s = enc.leftAdjustCharHead(text, adjustText, s, textEnd);
             }
-            
+
             while (s >= textP) {
                 int p = s;
                 int t = targetP;
@@ -366,14 +389,14 @@ public abstract class SearchAlgorithm {
                     p++; t++;
                 }
                 if (t == targetEnd) return s;
-                
+
                 s -= regex.intMapBackward[text[s] & 0xff];
                 s = enc.leftAdjustCharHead(text, adjustText, s, textEnd);
             }
-            return -1;            
+            return -1;
         }
-        
-        
+
+
         private void setBmBackwardSkip(Regex regex, byte[]bytes, int p, int end) {
             int[] skip;
             if (regex.intMapBackward == null) {
@@ -382,52 +405,51 @@ public abstract class SearchAlgorithm {
             } else {
                 skip = regex.intMapBackward;
             }
-            
+
             int len = end - p;
-            
+
             for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) skip[i] = len;
             for (int i=len-1; i>0; i--) skip[bytes[i] & 0xff] = i;
-        }        
+        }
     };
-    
+
     public static final SearchAlgorithm BM_NOT_REV = new SearchAlgorithm() {
-        
+
         public final String getName() {
             return "EXACT_BM_NOT_REV";
-        }        
-        
+        }
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             Encoding enc = regex.enc;
             byte[]target = regex.exact;
             int targetP = regex.exactP;
-            int targetEnd = regex.exactEnd;              
-            
+            int targetEnd = regex.exactEnd;
+
             int tail = targetEnd - 1;
             int tlen1 = tail - targetP;
             int end = textRange;
-            
+
             if (Config.DEBUG_SEARCH) {
                 Config.log.println("bm_search_notrev: "+
                                     "text: " + textP +
                                     ", text_end: " + textEnd +
                                     ", text_range: " + textRange);
             }
-            
+
             if (end + tlen1 > textEnd) end = textEnd - tlen1;
-            
+
             int s = textP;
-            
+
             if (regex.intMap == null) {
                 while (s < end) {
                     int p, se;
                     p = se = s + tlen1;
                     int t = tail;
-                    while (t >= targetP && text[p] == target[t]) {
+                    while (text[p] == target[t]) {
+                        if (t == targetP) return s;
                         p--; t--;
                     }
-                    
-                    if (t < targetP) return s;
-                    
+
                     int skip = regex.map[text[se] & 0xff];
                     t = s;
                     do {
@@ -439,35 +461,35 @@ public abstract class SearchAlgorithm {
                     int p, se;
                     p = se = s + tlen1;
                     int t = tail;
-                    while (t >= targetP && text[p] == target[t]) {
+
+                    while (text[p] == target[t]) {
+                        if (t == targetP) return s;
                         p--; t--;
                     }
-                    
-                    if (t < targetP) return s;
-                    
+
                     int skip = regex.intMap[text[se] & 0xff];
                     t = s;
                     do {
                         s += enc.length(text, s, textEnd);
                     } while ((s - t) < skip && s < end);
-                    
+
                 }
             }
-            return -1;            
+            return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             return BM.searchBackward(regex, text, textP, adjustText, textEnd, textStart, s_, range_);
         }
     };
-    
-    
+
+
     public static final SearchAlgorithm MAP = new SearchAlgorithm() {
 
         public final String getName() {
             return "MAP";
-        }        
-        
+        }
+
         // TODO: check 1.9 inconsistent calls to map_search
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             Encoding enc = regex.enc;
@@ -480,13 +502,13 @@ public abstract class SearchAlgorithm {
             }
             return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             Encoding enc = regex.enc;
             byte[]map = regex.map;
             int s = textStart;
 
-            if (s >= textEnd) s = textEnd - 1; // multibyte safe ? 
+            if (s >= textEnd) s = textEnd - 1; // multibyte safe ?
             while (s >= textP) {
                 if (map[text[s] & 0xff] != 0) return s;
                 s = enc.prevCharHead(text, adjustText, s, textEnd);
@@ -499,30 +521,30 @@ public abstract class SearchAlgorithm {
 
         public final String getName() {
             return "MAP_SB";
-        }        
-        
+        }
+
         public final int search(Regex regex, byte[]text, int textP, int textEnd, int textRange) {
             byte[]map = regex.map;
             int s = textP;
-            
+
             while (s < textRange) {
                 if (map[text[s] & 0xff] != 0) return s;
                 s++;
             }
             return -1;
         }
-        
+
         public final int searchBackward(Regex regex, byte[]text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_) {
             byte[]map = regex.map;
             int s = textStart;
 
-            if (s >= textEnd) s = textEnd - 1; 
+            if (s >= textEnd) s = textEnd - 1;
             while (s >= textP) {
                 if (map[text[s] & 0xff] != 0) return s;
                 s--;
             }
             return -1;
         }
-    };   
-    
+    };
+
 }
diff --git a/src/org/joni/StackEntry.java b/src/org/joni/StackEntry.java
index 001c98d..81b1785 100644
--- a/src/org/joni/StackEntry.java
+++ b/src/org/joni/StackEntry.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -22,7 +22,7 @@ package org.joni;
 final class StackEntry {
     int type;
     private int E1, E2, E3, E4;
-    
+
     // first union member
     /* byte code position */
     void setStatePCode(int pcode) {
@@ -52,7 +52,7 @@ final class StackEntry {
     int getStateCheck() {
         return E4;
     }
-    
+
     // second union member
     /* for OP_REPEAT_INC, OP_REPEAT_INC_NG */
     void setRepeatCount(int count) {
@@ -63,7 +63,7 @@ final class StackEntry {
     }
     void decreaseRepeatCount() {
         E1--;
-    }    
+    }
     void increaseRepeatCount() {
         E1++;
     }
@@ -81,7 +81,7 @@ final class StackEntry {
     int getRepeatNum() {
         return E3;
     }
-    
+
     // third union member
     /* index of stack */ /*int repeat_inc struct*/
     void setSi(int si) {
@@ -90,7 +90,7 @@ final class StackEntry {
     int getSi() {
         return E1;
     }
-    
+
     // fourth union member
     /* memory num */
     void setMemNum(int num) {
@@ -106,7 +106,7 @@ final class StackEntry {
     int getMemPStr() {
         return E2;
     }
-    
+
     /* Following information is set, if this stack type is MEM-START */
     /* prev. info (for backtrack  "(...)*" ) */
     void setMemStart(int start) {
@@ -122,7 +122,7 @@ final class StackEntry {
     int getMemEnd() {
         return E4;
     }
-    
+
     // fifth union member
     /* null check id */
     void setNullCheckNum(int num) {
@@ -137,8 +137,8 @@ final class StackEntry {
     }
     int getNullCheckPStr() {
         return E2;
-    } 
-    
+    }
+
     // sixth union member
     /* byte code position */
     void setCallFrameRetAddr(int addr) {
@@ -158,7 +158,7 @@ final class StackEntry {
     void setCallFramePStr(int pstr) {
         E3 = pstr;
     }
-    int getCallFramePStr() {        
-        return E3;        
+    int getCallFramePStr() {
+        return E3;
     }
 }
diff --git a/src/org/joni/StackMachine.java b/src/org/joni/StackMachine.java
index 7fe60a7..a5e51a4 100644
--- a/src/org/joni/StackMachine.java
+++ b/src/org/joni/StackMachine.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -29,7 +29,7 @@ import org.joni.constants.StackType;
 
 abstract class StackMachine extends Matcher implements StackType {
     protected static final int INVALID_INDEX = -1;
-    
+
     protected StackEntry[]stack;
     protected int stk;  // stkEnd
 
@@ -38,7 +38,7 @@ abstract class StackMachine extends Matcher implements StackType {
 
     // CEC
     protected byte[] stateCheckBuff; // move to int[] ?
-    int stateCheckBuffSize;    
+    int stateCheckBuffSize;
 
     protected StackMachine(Regex regex, byte[]bytes, int p , int end) {
         super(regex, bytes, p, end);
@@ -46,7 +46,7 @@ abstract class StackMachine extends Matcher implements StackType {
         this.stack = regex.stackNeeded ? fetchStack() : null;
         int n = regex.numRepeat + (regex.numMem << 1);
         this.repeatStk = n > 0 ? new int[n] : null;
-        
+
         memStartStk = regex.numRepeat - 1;
         memEndStk   = memStartStk + regex.numMem;
         /* for index start from 1, mem_start_stk[1]..mem_start_stk[num_mem] */
@@ -58,13 +58,13 @@ abstract class StackMachine extends Matcher implements StackType {
         stack[0] = new StackEntry();
         return stack;
     }
-    
+
     private void doubleStack() {
         StackEntry[] newStack = new StackEntry[stack.length << 1];
         System.arraycopy(stack, 0, newStack, 0, stack.length);
         stack = newStack;
-    }    
-    
+    }
+
     static final ThreadLocal<WeakReference<StackEntry[]>> stacks
             = new ThreadLocal<WeakReference<StackEntry[]>>() {
         @Override
@@ -86,7 +86,7 @@ abstract class StackMachine extends Matcher implements StackType {
     protected final void init() {
         if (stack != null) pushEnsured(ALT, regex.codeLength - 1); /* bottom stack */
         if (repeatStk != null) {
-            for (int i=1; i<=regex.numMem; i++) { 
+            for (int i=1; i<=regex.numMem; i++) {
                 repeatStk[i + memStartStk] = repeatStk[i + memEndStk] = INVALID_INDEX;
             }
         }
@@ -98,7 +98,7 @@ abstract class StackMachine extends Matcher implements StackType {
         if (e == null) stack[stk] = e = new StackEntry();
         return e;
     }
-    
+
     protected final void pushType(int type) {
         ensure1().type = type;
         stk++;
@@ -124,7 +124,7 @@ abstract class StackMachine extends Matcher implements StackType {
     private void stateCheckMark() {
         StackEntry e = stack[stk];
         int x = stateCheckPos(e.getStatePStr(), e.getStateCheck());
-        stateCheckBuff[x / 8] |= (1 << (x % 8)); 
+        stateCheckBuff[x / 8] |= (1 << (x % 8));
     }
 
     // STATE_CHECK_BUFF_INIT
@@ -133,16 +133,16 @@ abstract class StackMachine extends Matcher implements StackType {
         if (stateNum > 0 && strLength >= Config.CHECK_STRING_THRESHOLD_LEN) {
             int size = ((strLength + 1) * stateNum + 7) >>> 3;
             offset = (offset * stateNum) >>> 3;
-            
+
             if (size > 0 && offset < size && size < Config.CHECK_BUFF_MAX_SIZE) {
                 if (size >= STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE) {
-                    stateCheckBuff = new byte[size]; 
+                    stateCheckBuff = new byte[size];
                 } else {
                     // same impl, reduce...
                     stateCheckBuff = new byte[size];
                 }
                 Arrays.fill(stateCheckBuff, offset, (size - offset), (byte)0);
-                stateCheckBuffSize = size;                
+                stateCheckBuffSize = size;
             } else {
                 stateCheckBuff = null; // reduce
                 stateCheckBuffSize = 0;
@@ -175,17 +175,17 @@ abstract class StackMachine extends Matcher implements StackType {
         if (Config.USE_COMBINATION_EXPLOSION_CHECK) e.setStateCheck(0);
         stk++;
     }
-    
+
     protected final void pushAltWithStateCheck(int pat, int s, int sprev, int snum) {
         StackEntry e = ensure1();
         e.type = ALT;
         e.setStatePCode(pat);
         e.setStatePStr(s);
-        e.setStatePStrPrev(sprev);        
-        if (Config.USE_COMBINATION_EXPLOSION_CHECK) e.setStateCheck(stateCheckBuff != null ? snum : 0);        
+        e.setStatePStrPrev(sprev);
+        if (Config.USE_COMBINATION_EXPLOSION_CHECK) e.setStateCheck(stateCheckBuff != null ? snum : 0);
         stk++;
     }
-    
+
     protected final void pushStateCheck(int s, int snum) {
         if (stateCheckBuff != null) {
             StackEntry e = ensure1();
@@ -193,30 +193,30 @@ abstract class StackMachine extends Matcher implements StackType {
             e.setStatePStr(s);
             e.setStateCheck(snum);
             stk++;
-        }        
+        }
     }
 
     protected final void pushAlt(int pat, int s, int prev) {
         push(ALT, pat, s, prev);
     }
-    
+
     protected final void pushPos(int s, int prev) {
         push(POS, -1 /*NULL_UCHARP*/, s, prev);
     }
-    
+
     protected final void pushPosNot(int pat, int s, int prev) {
         push(POS_NOT, pat, s, prev);
     }
-    
-    protected final void pushStopBT() { 
+
+    protected final void pushStopBT() {
         pushType(STOP_BT);
     }
-    
+
     protected final void pushLookBehindNot(int pat, int s, int sprev) {
         push(LOOK_BEHIND_NOT, pat, s, sprev);
     }
-    
-    protected final void pushRepeat(int id, int pat) {        
+
+    protected final void pushRepeat(int id, int pat) {
         StackEntry e = ensure1();
         e.type = REPEAT;
         e.setRepeatNum(id);
@@ -224,14 +224,14 @@ abstract class StackMachine extends Matcher implements StackType {
         e.setRepeatCount(0);
         stk++;
     }
-    
+
     protected final void pushRepeatInc(int sindex) {
         StackEntry e = ensure1();
         e.type = REPEAT_INC;
         e.setSi(sindex);
         stk++;
     }
-    
+
     protected final void pushMemStart(int mnum, int s) {
         StackEntry e = ensure1();
         e.type = MEM_START;
@@ -239,7 +239,7 @@ abstract class StackMachine extends Matcher implements StackType {
         e.setMemPstr(s);
         e.setMemStart(repeatStk[memStartStk + mnum]);
         e.setMemEnd(repeatStk[memEndStk + mnum]);
-        repeatStk[memStartStk + mnum] = stk; 
+        repeatStk[memStartStk + mnum] = stk;
         repeatStk[memEndStk + mnum] = INVALID_INDEX;
         stk++;
     }
@@ -247,25 +247,25 @@ abstract class StackMachine extends Matcher implements StackType {
     protected final void pushMemEnd(int mnum, int s) {
         StackEntry e = ensure1();
         e.type = MEM_END;
-        e.setMemNum(mnum);        
+        e.setMemNum(mnum);
         e.setMemPstr(s);
         e.setMemStart(repeatStk[memStartStk + mnum]);
         e.setMemEnd(repeatStk[memEndStk + mnum]);
         repeatStk[memEndStk + mnum] = stk;
         stk++;
-    }    
-    
+    }
+
     protected final void pushMemEndMark(int mnum) {
         StackEntry e = ensure1();
         e.type = MEM_END_MARK;
         e.setMemNum(mnum);
         stk++;
     }
-    
+
     protected final int getMemStart(int mnum) {
         int level = 0;
         int stkp = stk;
-        
+
         while (stkp > 0) {
             stkp--;
             StackEntry e = stack[stkp];
@@ -278,7 +278,7 @@ abstract class StackMachine extends Matcher implements StackType {
         }
         return stkp;
     }
-    
+
     protected final void pushNullCheckStart(int cnum, int s) {
         StackEntry e = ensure1();
         e.type = NULL_CHECK_START;
@@ -286,30 +286,30 @@ abstract class StackMachine extends Matcher implements StackType {
         e.setNullCheckPStr(s);
         stk++;
     }
-    
+
     protected final void pushNullCheckEnd(int cnum) {
         StackEntry e = ensure1();
         e.type = NULL_CHECK_END;
         e.setNullCheckNum(cnum);
         stk++;
     }
-    
+
     protected final void pushCallFrame(int pat) {
         StackEntry e = ensure1();
         e.type = CALL_FRAME;
         e.setCallFrameRetAddr(pat);
         stk++;
     }
-    
+
     protected final void pushReturn() {
         StackEntry e = ensure1();
         e.type = RETURN;
         stk++;
     }
-    
+
     // stack debug routines here
     // ...
-    
+
     protected final void popOne() {
         stk--;
     }
@@ -328,7 +328,7 @@ abstract class StackMachine extends Matcher implements StackType {
     private StackEntry popFree() {
         while (true) {
             StackEntry e = stack[--stk];
-            
+
             if ((e.type & MASK_POP_USED) != 0) {
                 return e;
             } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
@@ -340,8 +340,8 @@ abstract class StackMachine extends Matcher implements StackType {
     private StackEntry popMemStart() {
         while (true) {
             StackEntry e = stack[--stk];
-            
-            if ((e.type & MASK_POP_USED) != 0) { 
+
+            if ((e.type & MASK_POP_USED) != 0) {
                 return e;
             } else if (e.type == MEM_START) {
                 repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
@@ -367,9 +367,9 @@ abstract class StackMachine extends Matcher implements StackType {
                 stack[e.getSi()].decreaseRepeatCount();
             } else if (e.type == MEM_END) {
                 repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
-                repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();                
-            } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {                    
-                if (e.type == STATE_CHECK_MARK) stateCheckMark();                    
+                repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();
+            } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                if (e.type == STATE_CHECK_MARK) stateCheckMark();
             }
         }
     }
@@ -378,7 +378,7 @@ abstract class StackMachine extends Matcher implements StackType {
         while (true) {
             stk--;
             StackEntry e = stack[stk];
-            
+
             if (e.type == POS_NOT) {
                 break;
             } else if (e.type == MEM_START) {
@@ -396,12 +396,12 @@ abstract class StackMachine extends Matcher implements StackType {
             }
         }
     }
-    
+
     protected final void popTilLookBehindNot() {
         while (true) {
             stk--;
             StackEntry e = stack[stk];
-            
+
             if (e.type == LOOK_BEHIND_NOT) {
                 break;
             } else if (e.type == MEM_START) {
@@ -419,7 +419,7 @@ abstract class StackMachine extends Matcher implements StackType {
             }
         }
     }
-    
+
     protected final int posEnd() {
         int k = stk;
         while (true) {
@@ -429,12 +429,12 @@ abstract class StackMachine extends Matcher implements StackType {
                 e.type = VOID;
             } else if (e.type == POS) {
                 e.type = VOID;
-                break;                
+                break;
             }
         }
         return k;
     }
-    
+
     protected final void stopBtEnd() {
         int k = stk;
         while (true) {
@@ -449,14 +449,14 @@ abstract class StackMachine extends Matcher implements StackType {
             }
         }
     }
-    
+
     // int for consistency with other null check routines
     protected final int nullCheck(int id, int s) {
         int k = stk;
         while (true) {
             k--;
             StackEntry e = stack[k];
-            
+
             if (e.type == NULL_CHECK_START) {
                 if (e.getNullCheckNum() == id) {
                     return e.getNullCheckPStr() == s ? 1 : 0;
@@ -464,14 +464,14 @@ abstract class StackMachine extends Matcher implements StackType {
             }
         }
     }
-    
+
     protected final int nullCheckRec(int id, int s) {
         int level = 0;
         int k = stk;
         while (true) {
             k--;
             StackEntry e = stack[k];
-            
+
             if (e.type == NULL_CHECK_START) {
                 if (e.getNullCheckNum() == id) {
                     if (level == 0) {
@@ -485,14 +485,14 @@ abstract class StackMachine extends Matcher implements StackType {
             }
         }
     }
-    
+
     protected final int nullCheckMemSt(int id, int s) {
         int k = stk;
         int isNull;
         while (true) {
             k--;
             StackEntry e = stack[k];
-            
+
             if (e.type == NULL_CHECK_START) {
                 if (e.getNullCheckNum() == id) {
                     if (e.getNullCheckPStr() != s) {
@@ -529,7 +529,7 @@ abstract class StackMachine extends Matcher implements StackType {
         }
         return isNull;
     }
-    
+
     protected final int nullCheckMemStRec(int id, int s) {
         int level = 0;
         int k = stk;
@@ -580,14 +580,14 @@ abstract class StackMachine extends Matcher implements StackType {
         }
         return isNull;
     }
-    
+
     protected final int getRepeat(int id) {
         int level = 0;
         int k = stk;
         while (true) {
             k--;
             StackEntry e = stack[k];
-            
+
             if (e.type == REPEAT) {
                 if (level == 0) {
                     if (e.getRepeatNum() == id) return k;
@@ -599,15 +599,15 @@ abstract class StackMachine extends Matcher implements StackType {
             }
         }
     }
-    
+
     protected final int sreturn() {
         int level = 0;
         int k = stk;
         while (true) {
             k--;
             StackEntry e = stack[k];
-            
-            if (e.type == CALL_FRAME) {            
+
+            if (e.type == CALL_FRAME) {
                 if (level == 0) {
                     return e.getCallFrameRetAddr();
                 } else {
diff --git a/src/org/joni/Syntax.java b/src/org/joni/Syntax.java
index b89abe9..74662a8 100644
--- a/src/org/joni/Syntax.java
+++ b/src/org/joni/Syntax.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -24,7 +24,7 @@ import static org.joni.constants.MetaChar.INEFFECTIVE_META_CHAR;
 import org.joni.constants.SyntaxProperties;
 
 public final class Syntax implements SyntaxProperties{
-    private final int op;   
+    private final int op;
     private final int op2;
     private final int behavior;
     public final int options;
@@ -39,273 +39,273 @@ public final class Syntax implements SyntaxProperties{
     }
 
     public static class MetaCharTable {
-        public final int esc;   
+        public final int esc;
         public final int anyChar;
         public final int anyTime;
         public final int zeroOrOneTime;
         public final int oneOrMoreTime;
         public final int anyCharAnyTime;
-        
-        public MetaCharTable(int esc, int anyChar, int anyTime, 
+
+        public MetaCharTable(int esc, int anyChar, int anyTime,
                              int zeroOrOneTime, int oneOrMoreTime, int anyCharAnyTime) {
             this.esc = esc;
             this.anyChar = anyChar;
             this.anyTime = anyTime;
             this.zeroOrOneTime = zeroOrOneTime;
-            this.oneOrMoreTime = oneOrMoreTime;  
+            this.oneOrMoreTime = oneOrMoreTime;
             this.anyCharAnyTime = anyCharAnyTime;
         }
     }
-    
+
     /**
      * OP
-     * 
+     *
      */
     protected boolean isOp(int opm) {
         return (op & opm) != 0;
     }
-    
-    public boolean opVariableMetaCharacters() { 
+
+    public boolean opVariableMetaCharacters() {
         return isOp(OP_VARIABLE_META_CHARACTERS);
     }
-    
-    public boolean opDotAnyChar() { 
+
+    public boolean opDotAnyChar() {
         return isOp(OP_DOT_ANYCHAR);
     }
 
-    public boolean opAsteriskZeroInf() { 
+    public boolean opAsteriskZeroInf() {
         return isOp(OP_ASTERISK_ZERO_INF);
     }
-    
-    public boolean opEscAsteriskZeroInf() { 
+
+    public boolean opEscAsteriskZeroInf() {
         return isOp(OP_ESC_ASTERISK_ZERO_INF);
     }
 
-    public boolean opPlusOneInf() { 
+    public boolean opPlusOneInf() {
         return isOp(OP_PLUS_ONE_INF);
     }
-    
-    public boolean opEscPlusOneInf() { 
+
+    public boolean opEscPlusOneInf() {
         return isOp(OP_ESC_PLUS_ONE_INF);
     }
 
-    public boolean opQMarkZeroOne() { 
+    public boolean opQMarkZeroOne() {
         return isOp(OP_QMARK_ZERO_ONE);
     }
-    
-    public boolean opEscQMarkZeroOne() { 
+
+    public boolean opEscQMarkZeroOne() {
         return isOp(OP_ESC_QMARK_ZERO_ONE);
     }
-    
-    public boolean opBraceInterval() { 
+
+    public boolean opBraceInterval() {
         return isOp(OP_BRACE_INTERVAL);
     }
-    
-    public boolean opEscBraceInterval() { 
+
+    public boolean opEscBraceInterval() {
         return isOp(OP_ESC_BRACE_INTERVAL);
     }
 
-    public boolean opVBarAlt() { 
+    public boolean opVBarAlt() {
         return isOp(OP_VBAR_ALT);
     }
 
-    public boolean opEscVBarAlt() { 
+    public boolean opEscVBarAlt() {
         return isOp(OP_ESC_VBAR_ALT);
     }
-    
-    public boolean opLParenSubexp() { 
+
+    public boolean opLParenSubexp() {
         return isOp(OP_LPAREN_SUBEXP);
     }
 
-    public boolean opEscLParenSubexp() { 
+    public boolean opEscLParenSubexp() {
         return isOp(OP_ESC_LPAREN_SUBEXP);
     }
-    
-    public boolean opEscAZBufAnchor() { 
+
+    public boolean opEscAZBufAnchor() {
         return isOp(OP_ESC_AZ_BUF_ANCHOR);
     }
 
-    public boolean opEscCapitalGBeginAnchor() { 
+    public boolean opEscCapitalGBeginAnchor() {
         return isOp(OP_ESC_CAPITAL_G_BEGIN_ANCHOR);
     }
-    
-    public boolean opDecimalBackref() { 
+
+    public boolean opDecimalBackref() {
         return isOp(OP_DECIMAL_BACKREF);
     }
 
-    public boolean opBracketCC() { 
+    public boolean opBracketCC() {
         return isOp(OP_BRACKET_CC);
     }
-    
-    public boolean opEscWWord() { 
+
+    public boolean opEscWWord() {
         return isOp(OP_ESC_W_WORD);
     }
 
-    public boolean opEscLtGtWordBeginEnd() { 
+    public boolean opEscLtGtWordBeginEnd() {
         return isOp(OP_ESC_LTGT_WORD_BEGIN_END);
     }
 
     public boolean opEscBWordBound() {
         return isOp(OP_ESC_B_WORD_BOUND);
     }
-    
+
     public boolean opEscSWhiteSpace() {
         return isOp(OP_ESC_S_WHITE_SPACE);
     }
-    
-    public boolean opEscDDigit() { 
+
+    public boolean opEscDDigit() {
         return isOp(OP_ESC_D_DIGIT);
     }
 
-    public boolean opLineAnchor() { 
+    public boolean opLineAnchor() {
         return isOp(OP_LINE_ANCHOR);
     }
-    
-    public boolean opPosixBracket() { 
+
+    public boolean opPosixBracket() {
         return isOp(OP_POSIX_BRACKET);
     }
-    
-    public boolean opQMarkNonGreedy() { 
+
+    public boolean opQMarkNonGreedy() {
         return isOp(OP_QMARK_NON_GREEDY);
     }
-    
-    public boolean opEscControlChars() { 
+
+    public boolean opEscControlChars() {
         return isOp(OP_ESC_CONTROL_CHARS);
     }
-    
-    public boolean opEscCControl() { 
+
+    public boolean opEscCControl() {
         return isOp(OP_ESC_C_CONTROL);
     }
 
-    public boolean opEscOctal3() { 
+    public boolean opEscOctal3() {
         return isOp(OP_ESC_OCTAL3);
     }
-    
-    public boolean opEscXHex2() { 
+
+    public boolean opEscXHex2() {
         return isOp(OP_ESC_X_HEX2);
     }
-    
-    public boolean opEscXBraceHex8() { 
+
+    public boolean opEscXBraceHex8() {
         return isOp(OP_ESC_X_BRACE_HEX8);
     }
-    
+
 
     /**
      * OP
-     * 
+     *
      */
     protected boolean isOp2(int opm) {
         return (op2 & opm) != 0;
     }
-    
-    public boolean op2EscCapitalQQuote() { 
+
+    public boolean op2EscCapitalQQuote() {
         return isOp2(OP2_ESC_CAPITAL_Q_QUOTE);
-    }   
-    
-    public boolean op2QMarkGroupEffect() { 
+    }
+
+    public boolean op2QMarkGroupEffect() {
         return isOp2(OP2_QMARK_GROUP_EFFECT);
-    }   
+    }
 
-    public boolean op2OptionPerl() { 
+    public boolean op2OptionPerl() {
         return isOp2(OP2_OPTION_PERL);
-    }   
+    }
 
-    public boolean op2OptionRuby() { 
+    public boolean op2OptionRuby() {
         return isOp2(OP2_OPTION_RUBY);
-    }   
-    
-    public boolean op2PlusPossessiveRepeat() { 
+    }
+
+    public boolean op2PlusPossessiveRepeat() {
         return isOp2(OP2_PLUS_POSSESSIVE_REPEAT);
-    }   
+    }
 
-    public boolean op2PlusPossessiveInterval() { 
+    public boolean op2PlusPossessiveInterval() {
         return isOp2(OP2_PLUS_POSSESSIVE_INTERVAL);
-    }   
-    
-    public boolean op2CClassSetOp() { 
+    }
+
+    public boolean op2CClassSetOp() {
         return isOp2(OP2_CCLASS_SET_OP);
-    }   
+    }
 
-    public boolean op2QMarkLtNamedGroup() { 
+    public boolean op2QMarkLtNamedGroup() {
         return isOp2(OP2_QMARK_LT_NAMED_GROUP);
-    }   
+    }
 
-    public boolean op2EscKNamedBackref() { 
+    public boolean op2EscKNamedBackref() {
         return isOp2(OP2_ESC_K_NAMED_BACKREF);
-    }   
-    
-    public boolean op2EscGSubexpCall() { 
+    }
+
+    public boolean op2EscGSubexpCall() {
         return isOp2(OP2_ESC_G_SUBEXP_CALL);
-    }   
+    }
 
-    public boolean op2AtMarkCaptureHistory() { 
+    public boolean op2AtMarkCaptureHistory() {
         return isOp2(OP2_ATMARK_CAPTURE_HISTORY);
-    }   
-    
-    public boolean op2EscCapitalCBarControl() { 
+    }
+
+    public boolean op2EscCapitalCBarControl() {
         return isOp2(OP2_ESC_CAPITAL_C_BAR_CONTROL);
-    }   
+    }
 
-    public boolean op2EscCapitalMBarMeta() { 
+    public boolean op2EscCapitalMBarMeta() {
         return isOp2(OP2_ESC_CAPITAL_M_BAR_META);
-    }   
-    
-    public boolean op2EscVVtab() { 
+    }
+
+    public boolean op2EscVVtab() {
         return isOp2(OP2_ESC_V_VTAB);
     }
-    
-    public boolean op2EscUHex4() { 
+
+    public boolean op2EscUHex4() {
         return isOp2(OP2_ESC_U_HEX4);
-    }   
-    
-    public boolean op2EscGnuBufAnchor() { 
+    }
+
+    public boolean op2EscGnuBufAnchor() {
         return isOp2(OP2_ESC_GNU_BUF_ANCHOR);
-    }   
-    
-    public boolean op2EscPBraceCharProperty() { 
+    }
+
+    public boolean op2EscPBraceCharProperty() {
         return isOp2(OP2_ESC_P_BRACE_CHAR_PROPERTY);
-    }   
+    }
 
-    public boolean op2EscPBraceCircumflexNot() { 
+    public boolean op2EscPBraceCircumflexNot() {
         return isOp2(OP2_ESC_P_BRACE_CIRCUMFLEX_NOT);
-    }   
+    }
 
-    public boolean op2EscHXDigit() { 
+    public boolean op2EscHXDigit() {
         return isOp2(OP2_ESC_H_XDIGIT);
-    }   
-    
-    public boolean op2IneffectiveEscape() { 
+    }
+
+    public boolean op2IneffectiveEscape() {
         return isOp2(OP2_INEFFECTIVE_ESCAPE);
-    }   
-    
+    }
+
     /**
      * BEHAVIOR
-     * 
+     *
      */
     protected boolean isBehavior(int bvm) {
         return (behavior & bvm) != 0;
     }
-    
+
     public boolean contextIndepRepeatOps() {
-        return isBehavior(CONTEXT_INDEP_REPEAT_OPS); 
+        return isBehavior(CONTEXT_INDEP_REPEAT_OPS);
     }
-    
+
     public boolean contextInvalidRepeatOps() {
-        return isBehavior(CONTEXT_INVALID_REPEAT_OPS); 
+        return isBehavior(CONTEXT_INVALID_REPEAT_OPS);
     }
-    
+
     public boolean allowUnmatchedCloseSubexp() {
-        return isBehavior(ALLOW_UNMATCHED_CLOSE_SUBEXP); 
+        return isBehavior(ALLOW_UNMATCHED_CLOSE_SUBEXP);
     }
-    
+
     public boolean allowInvalidInterval() {
-        return isBehavior(ALLOW_INVALID_INTERVAL); 
+        return isBehavior(ALLOW_INVALID_INTERVAL);
     }
 
     public boolean allowIntervalLowAbbrev() {
-        return isBehavior(ALLOW_INTERVAL_LOW_ABBREV); 
+        return isBehavior(ALLOW_INTERVAL_LOW_ABBREV);
     }
-    
+
     public boolean strictCheckBackref() {
         return isBehavior(STRICT_CHECK_BACKREF);
     }
@@ -313,7 +313,7 @@ public final class Syntax implements SyntaxProperties{
     public boolean differentLengthAltLookBehind() {
         return isBehavior(DIFFERENT_LEN_ALT_LOOK_BEHIND);
     }
-    
+
     public boolean captureOnlyNamedGroup() {
         return isBehavior(CAPTURE_ONLY_NAMED_GROUP);
     }
@@ -325,8 +325,8 @@ public final class Syntax implements SyntaxProperties{
     public boolean fixedIntervalIsGreedyOnly() {
         return isBehavior(FIXED_INTERVAL_IS_GREEDY_ONLY);
     }
-    
-    
+
+
     public boolean notNewlineInNegativeCC() {
         return isBehavior(NOT_NEWLINE_IN_NEGATIVE_CC);
     }
@@ -334,30 +334,30 @@ public final class Syntax implements SyntaxProperties{
     public boolean backSlashEscapeInCC() {
         return isBehavior(BACKSLASH_ESCAPE_IN_CC);
     }
-    
+
     public boolean allowEmptyRangeInCC() {
         return isBehavior(ALLOW_EMPTY_RANGE_IN_CC);
     }
-    
+
     public boolean allowDoubleRangeOpInCC() {
         return isBehavior(ALLOW_DOUBLE_RANGE_OP_IN_CC);
     }
-    
+
     public boolean warnCCOpNotEscaped() {
         return isBehavior(WARN_CC_OP_NOT_ESCAPED);
     }
-    
+
     public boolean warnReduntantNestedRepeat() {
         return isBehavior(WARN_REDUNDANT_NESTED_REPEAT);
     }
-    
+
     public static final Syntax RUBY = new Syntax(
         (( GNU_REGEX_OP | OP_QMARK_NON_GREEDY |
         OP_ESC_OCTAL3 | OP_ESC_X_HEX2 |
         OP_ESC_X_BRACE_HEX8 | OP_ESC_CONTROL_CHARS |
         OP_ESC_C_CONTROL )
         & ~OP_ESC_LTGT_WORD_BEGIN_END ),
-        
+
         ( OP2_QMARK_GROUP_EFFECT |
         OP2_OPTION_RUBY |
         OP2_QMARK_LT_NAMED_GROUP | OP2_ESC_K_NAMED_BACKREF |
@@ -368,8 +368,8 @@ public final class Syntax implements SyntaxProperties{
         OP2_CCLASS_SET_OP | OP2_ESC_CAPITAL_C_BAR_CONTROL |
         OP2_ESC_CAPITAL_M_BAR_META | OP2_ESC_V_VTAB |
         OP2_ESC_H_XDIGIT ),
-        
-        ( GNU_REGEX_BV | 
+
+        ( GNU_REGEX_BV |
         ALLOW_INTERVAL_LOW_ABBREV |
         DIFFERENT_LEN_ALT_LOOK_BEHIND |
         CAPTURE_ONLY_NAMED_GROUP |
@@ -377,9 +377,9 @@ public final class Syntax implements SyntaxProperties{
         FIXED_INTERVAL_IS_GREEDY_ONLY |
         WARN_CC_OP_NOT_ESCAPED |
         WARN_REDUNDANT_NESTED_REPEAT ),
-        
+
         Option.NONE,
-        
+
         new MetaCharTable(
             '\\',                           /* esc */
             INEFFECTIVE_META_CHAR,          /* anychar '.' */
@@ -389,14 +389,14 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR           /* anychar anytime */
         )
     );
-    
+
     public static final Syntax DEFAULT = RUBY;
-    
+
     public static final Syntax ASIS = new Syntax(
         0,
 
         OP2_INEFFECTIVE_ESCAPE,
-        
+
         0,
 
         Option.NONE,
@@ -410,17 +410,17 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR          /* anychar anytime */
         )
     );
-    
+
     public static final Syntax PosixBasic = new Syntax(
         (POSIX_COMMON_OP | OP_ESC_LPAREN_SUBEXP |
         OP_ESC_BRACE_INTERVAL ),
-        
+
         0,
-        
+
         0,
- 
+
         ( Option.SINGLELINE | Option.MULTILINE ),
-         
+
         new MetaCharTable(
             '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
@@ -428,23 +428,23 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )         
+        )
     );
-    
+
     public static final Syntax PosixExtended = new Syntax(
         ( POSIX_COMMON_OP | OP_LPAREN_SUBEXP |
         OP_BRACE_INTERVAL |
         OP_PLUS_ONE_INF | OP_QMARK_ZERO_ONE |OP_VBAR_ALT ),
-        
+
         0,
-        
-        ( CONTEXT_INDEP_ANCHORS | 
-        CONTEXT_INDEP_REPEAT_OPS | CONTEXT_INVALID_REPEAT_OPS | 
+
+        ( CONTEXT_INDEP_ANCHORS |
+        CONTEXT_INDEP_REPEAT_OPS | CONTEXT_INVALID_REPEAT_OPS |
         ALLOW_UNMATCHED_CLOSE_SUBEXP |
         ALLOW_DOUBLE_RANGE_OP_IN_CC ),
-        
-        ( Option.SINGLELINE | Option.MULTILINE ),            
-            
+
+        ( Option.SINGLELINE | Option.MULTILINE ),
+
         new MetaCharTable(
             '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
@@ -452,9 +452,9 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )            
+        )
     );
-    
+
     public static final Syntax Emacs = new Syntax(
         ( OP_DOT_ANYCHAR | OP_BRACKET_CC |
         OP_ESC_BRACE_INTERVAL |
@@ -462,13 +462,13 @@ public final class Syntax implements SyntaxProperties{
         OP_ASTERISK_ZERO_INF | OP_PLUS_ONE_INF |
         OP_QMARK_ZERO_ONE | OP_DECIMAL_BACKREF |
         OP_LINE_ANCHOR | OP_ESC_CONTROL_CHARS ),
-        
+
         OP2_ESC_GNU_BUF_ANCHOR,
-        
+
         ALLOW_EMPTY_RANGE_IN_CC,
 
         Option.NONE,
-        
+
         new MetaCharTable(
             '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
@@ -476,9 +476,9 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )        
+        )
     );
-    
+
     public static final Syntax Grep = new Syntax(
         ( OP_DOT_ANYCHAR | OP_BRACKET_CC | OP_POSIX_BRACKET |
         OP_ESC_BRACE_INTERVAL | OP_ESC_LPAREN_SUBEXP |
@@ -487,13 +487,13 @@ public final class Syntax implements SyntaxProperties{
         OP_ESC_QMARK_ZERO_ONE | OP_LINE_ANCHOR |
         OP_ESC_W_WORD | OP_ESC_B_WORD_BOUND |
         OP_ESC_LTGT_WORD_BEGIN_END | OP_DECIMAL_BACKREF ),
-        
+
         0,
-        
+
         ( ALLOW_EMPTY_RANGE_IN_CC | NOT_NEWLINE_IN_NEGATIVE_CC ),
 
         Option.NONE,
-        
+
         new MetaCharTable(
             '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
@@ -501,14 +501,14 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )          
+        )
     );
-    
+
     public static final Syntax GnuRegex = new Syntax(
         GNU_REGEX_OP,
         0,
         GNU_REGEX_BV,
-        
+
         Option.NONE,
 
         new MetaCharTable(
@@ -520,23 +520,23 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR          /* anychar anytime */
         )
     );
-    
+
     public static final Syntax Java = new Syntax(
         (( GNU_REGEX_OP | OP_QMARK_NON_GREEDY |
         OP_ESC_CONTROL_CHARS | OP_ESC_C_CONTROL |
         OP_ESC_OCTAL3 | OP_ESC_X_HEX2 )
         & ~OP_ESC_LTGT_WORD_BEGIN_END ),
-        
+
         ( OP2_ESC_CAPITAL_Q_QUOTE | OP2_QMARK_GROUP_EFFECT |
         OP2_OPTION_PERL | OP2_PLUS_POSSESSIVE_REPEAT |
         OP2_PLUS_POSSESSIVE_INTERVAL | OP2_CCLASS_SET_OP |
         OP2_ESC_V_VTAB | OP2_ESC_U_HEX4 |
         OP2_ESC_P_BRACE_CHAR_PROPERTY ),
-        
+
         ( GNU_REGEX_BV | DIFFERENT_LEN_ALT_LOOK_BEHIND ),
-        
+
         Option.SINGLELINE,
-                 
+
         new MetaCharTable(
             '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
@@ -544,9 +544,9 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )                 
+        )
     );
-    
+
     public static final Syntax Perl = new Syntax(
         (( GNU_REGEX_OP | OP_QMARK_NON_GREEDY |
         OP_ESC_OCTAL3 | OP_ESC_X_HEX2 |
@@ -558,11 +558,11 @@ public final class Syntax implements SyntaxProperties{
         OP2_QMARK_GROUP_EFFECT | OP2_OPTION_PERL |
         OP2_ESC_P_BRACE_CHAR_PROPERTY |
         OP2_ESC_P_BRACE_CIRCUMFLEX_NOT ),
-        
+
         GNU_REGEX_BV,
-        
+
         Option.SINGLELINE,
-        
+
         new MetaCharTable(
             '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
@@ -570,16 +570,16 @@ public final class Syntax implements SyntaxProperties{
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )        
+        )
     );
-    
+
     public static final Syntax PerlNG = new Syntax(
         (( GNU_REGEX_OP | OP_QMARK_NON_GREEDY |
         OP_ESC_OCTAL3 | OP_ESC_X_HEX2 |
         OP_ESC_X_BRACE_HEX8 | OP_ESC_CONTROL_CHARS |
         OP_ESC_C_CONTROL )
         & ~OP_ESC_LTGT_WORD_BEGIN_END ),
-        
+
         ( OP2_ESC_CAPITAL_Q_QUOTE |
         OP2_QMARK_GROUP_EFFECT | OP2_OPTION_PERL |
         OP2_ESC_P_BRACE_CHAR_PROPERTY  |
@@ -587,20 +587,20 @@ public final class Syntax implements SyntaxProperties{
         OP2_QMARK_LT_NAMED_GROUP       |
         OP2_ESC_K_NAMED_BACKREF        |
         OP2_ESC_G_SUBEXP_CALL ),
-        
+
         ( GNU_REGEX_BV |
         CAPTURE_ONLY_NAMED_GROUP |
         ALLOW_MULTIPLEX_DEFINITION_NAME ),
-        
+
         Option.SINGLELINE,
-        
+
         new MetaCharTable(
-            '\\',                          /* esc */            
+            '\\',                          /* esc */
             INEFFECTIVE_META_CHAR,         /* anychar '.' */
             INEFFECTIVE_META_CHAR,         /* anytime '*' */
             INEFFECTIVE_META_CHAR,         /* zero or one time '?' */
             INEFFECTIVE_META_CHAR,         /* one or more time '+' */
             INEFFECTIVE_META_CHAR          /* anychar anytime */
-        )         
+        )
     );
 }
diff --git a/src/org/joni/Token.java b/src/org/joni/Token.java
index 16e2b1a..8ad7330 100644
--- a/src/org/joni/Token.java
+++ b/src/org/joni/Token.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -153,7 +153,7 @@ final class Token {
     }
     void setCallGNum(int gnum) {
         INT3 = gnum;
-    }    
+    }
 
     // prop union member
     int getPropCType() {
diff --git a/src/org/joni/UnsetAddrList.java b/src/org/joni/UnsetAddrList.java
index 8787972..34e472d 100644
--- a/src/org/joni/UnsetAddrList.java
+++ b/src/org/joni/UnsetAddrList.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -28,14 +28,14 @@ public final class UnsetAddrList {
     int num;
     Node[]targets;
     int[]offsets;
-    
+
     public UnsetAddrList(int size) {
         targets = new Node[size];
         offsets = new int[size];
     }
-    
+
     public void add(int offset, Node node) {
-        if (num >= offsets.length) {            
+        if (num >= offsets.length) {
             Node []ttmp = new Node[targets.length << 1];
             System.arraycopy(targets, 0, ttmp, 0, num);
             targets = ttmp;
@@ -45,10 +45,10 @@ public final class UnsetAddrList {
         }
         targets[num] = node;
         offsets[num] = offset;
-        
+
         num++;
     }
-    
+
     public void fix(Regex regex) {
         for (int i=0; i<num; i++) {
             EncloseNode en = (EncloseNode)targets[i];
@@ -56,7 +56,7 @@ public final class UnsetAddrList {
             regex.code[offsets[i]] = en.callAddr; // is this safe ?
         }
     }
-    
+
     public String toString() {
         StringBuilder value = new StringBuilder();
         if (num > 0) {
diff --git a/src/org/joni/WarnCallback.java b/src/org/joni/WarnCallback.java
index 351146b..b5f2a27 100644
--- a/src/org/joni/WarnCallback.java
+++ b/src/org/joni/WarnCallback.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
@@ -23,10 +23,11 @@ package org.joni;
  * @author <a href="mailto:ola.bini at gmail.com">Ola Bini</a>
  */
 public interface WarnCallback {
-    WarnCallback DEFAULT = new WarnCallback(){
-            public void warn(String message) {
-                System.err.println(message);
-            }
-        };
+    WarnCallback DEFAULT = new WarnCallback() {
+        public void warn(String message) {
+            System.err.println(message);
+        }
+    };
+
     void warn(String message);
-}// WarnCallback
+}
diff --git a/src/org/joni/NativeMachine.java b/src/org/joni/Warnings.java
similarity index 69%
copy from src/org/joni/NativeMachine.java
copy to src/org/joni/Warnings.java
index 6fc5dbb..80e1519 100644
--- a/src/org/joni/NativeMachine.java
+++ b/src/org/joni/Warnings.java
@@ -1,27 +1,26 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni;
 
-public abstract class NativeMachine extends Matcher {
-
-    protected NativeMachine(Regex regex, byte[]bytes, int p, int end) {
-        super(regex, bytes, p, end);
-    }
+public interface Warnings {
+    final String INVALID_BACKREFERENCE =            "invalid back reference";
+    final String INVALID_SUBEXP_CALL =              "invalid subexp call";
+    final String INVALID_UNICODE_PROPERTY =         "invalid Unicode Property \\<%n>";
 }
diff --git a/src/org/joni/ast/AnchorNode.java b/src/org/joni/ast/AnchorNode.java
index cccbc49..988d994 100644
--- a/src/org/joni/ast/AnchorNode.java
+++ b/src/org/joni/ast/AnchorNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -30,32 +30,32 @@ public final class AnchorNode extends Node implements AnchorType {
         this.type = type;
         charLength = -1;
     }
-    
+
     @Override
     public int getType() {
         return ANCHOR;
     }
-    
+
     @Override
     protected void setChild(Node newChild) {
         target = newChild;
     }
-    
+
     @Override
     protected Node getChild() {
         return target;
-    }    
-    
+    }
+
     public void setTarget(Node tgt) {
         target = tgt;
-        tgt.parent = this; 
+        tgt.parent = this;
     }
-    
+
     @Override
     public String getName() {
         return "Anchor";
     }
-    
+
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder();
@@ -63,7 +63,7 @@ public final class AnchorNode extends Node implements AnchorType {
         value.append("\n  target: " + pad(target, level + 1));
         return value.toString();
     }
-    
+
     public String typeToString() {
         StringBuilder type = new StringBuilder();
         if (isType(BEGIN_BUF)) type.append("BEGIN_BUF ");
@@ -82,11 +82,11 @@ public final class AnchorNode extends Node implements AnchorType {
         if (isType(LOOK_BEHIND_NOT)) type.append("LOOK_BEHIND_NOT ");
         if (isType(ANYCHAR_STAR)) type.append("ANYCHAR_STAR ");
         if (isType(ANYCHAR_STAR_ML)) type.append("ANYCHAR_STAR_ML ");
-        return type.toString();        
+        return type.toString();
     }
 
     private boolean isType(int type) {
-        return (this.type & type) != 0; 
+        return (this.type & type) != 0;
     }
 
 }
diff --git a/src/org/joni/ast/AnyCharNode.java b/src/org/joni/ast/AnyCharNode.java
index d349d8c..5b4f9b6 100644
--- a/src/org/joni/ast/AnyCharNode.java
+++ b/src/org/joni/ast/AnyCharNode.java
@@ -1,40 +1,40 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
 
 public final class AnyCharNode extends Node {
     public AnyCharNode(){}
-    
+
     @Override
     public int getType() {
         return CANY;
     }
-    
+
     @Override
     public String getName() {
         return "Any Char";
-    }   
-    
+    }
+
     @Override
     public String toString(int level) {
         String value = "";
         return value;
-    }   
+    }
 }
diff --git a/src/org/joni/ast/BackRefNode.java b/src/org/joni/ast/BackRefNode.java
index 040fb81..603cbe7 100644
--- a/src/org/joni/ast/BackRefNode.java
+++ b/src/org/joni/ast/BackRefNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -24,49 +24,49 @@ import org.joni.exception.ErrorMessages;
 import org.joni.exception.ValueException;
 
 public final class BackRefNode extends StateNode {
-    //private static int NODE_BACKREFS_SIZE = 6; 
-    
+    //private static int NODE_BACKREFS_SIZE = 6;
+
     //int state;
     public int backNum;
     public int back[];
-    
+
     public int nestLevel;
-    
+
     public BackRefNode(int backNum, int[]backRefs, boolean byName, ScanEnvironment env) {
         this.backNum = backNum;
         if (byName) setNameRef();
-        
+
         for (int i=0; i<backNum; i++) {
             if (backRefs[i] <= env.numMem && env.memNodes[backRefs[i]] == null) {
                 setRecursion(); /* /...(\1).../ */
                 break;
             }
         }
-        
+
         back = new int[backNum];
         System.arraycopy(backRefs, 0, back, 0, backNum); // shall we really dup it ???
     }
-    
+
     // #ifdef USE_BACKREF_AT_LEVEL
     public BackRefNode(int backNum, int[]backRefs, boolean byName, boolean existLevel, int nestLevel, ScanEnvironment env) {
         this(backNum, backRefs, byName, env);
-        
+
         if (existLevel) {
             //state |= NST_NEST_LEVEL;
             setNestLevel();
             this.nestLevel = nestLevel;
         }
     }
-    
+
     @Override
     public int getType() {
         return BREF;
-    }   
-    
+    }
+
     @Override
     public String getName() {
         return "Back Ref";
-    }   
+    }
 
     @Override
     public String toString(int level) {
@@ -78,12 +78,12 @@ public final class BackRefNode extends StateNode {
         value.append("\n  nextLevel: " + nestLevel);
         return value.toString();
     }
-    
+
     public void renumber(int[]map) {
         if (!isNameRef()) throw new ValueException(ErrorMessages.ERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED);
-        
+
         int oldNum = backNum;
-        
+
         int pos = 0;
         for (int i=0; i<oldNum; i++) {
             int n = map[back[i]];
@@ -94,5 +94,5 @@ public final class BackRefNode extends StateNode {
         }
         backNum = pos;
     }
-    
+
 }
diff --git a/src/org/joni/ast/CClassNode.java b/src/org/joni/ast/CClassNode.java
index 40c7c90..7927023 100644
--- a/src/org/joni/ast/CClassNode.java
+++ b/src/org/joni/ast/CClassNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -24,6 +24,7 @@ import org.jcodings.Encoding;
 import org.jcodings.IntHolder;
 import org.jcodings.constants.CharacterType;
 import org.jcodings.exception.EncodingException;
+import org.jcodings.specific.ASCIIEncoding;
 import org.joni.BitSet;
 import org.joni.CodeRangeBuffer;
 import org.joni.Config;
@@ -38,29 +39,35 @@ import org.joni.exception.ValueException;
 public final class CClassNode extends Node {
     private static final int FLAG_NCCLASS_NOT = 1<<0;
     private static final int FLAG_NCCLASS_SHARE = 1<<1;
-    
+
     int flags;
     public final BitSet bs = new BitSet();  // conditional creation ?
     public CodeRangeBuffer mbuf;            /* multi-byte info or NULL */
-    
+
     private int ctype;                      // for hashing purposes
-    private Encoding enc;                   // ... 
+    private Encoding enc;                   // ...
 
 
     // node_new_cclass
     public CClassNode() {}
-    
+
     public CClassNode(int ctype, Encoding enc, boolean not, int sbOut, int[]ranges) {
         this(not, sbOut, ranges);
         this.ctype = ctype;
         this.enc = enc;
     }
-    
+
+    public void clear() {
+        bs.clear();
+        flags = 0;
+        mbuf = null;
+    }
+
     // node_new_cclass_by_codepoint_range, only used by shared Char Classes
     public CClassNode(boolean not, int sbOut, int[]ranges) {
         if (not) setNot();
         // bs.clear();
-        
+
         if (sbOut > 0 && ranges != null) {
             int n = ranges[0];
             for (int i=0; i<n; i++) {
@@ -71,30 +78,30 @@ public final class CClassNode extends Node {
                         setupBuffer(ranges);
                         return;
                     }
-                    bs.set(j);                  
+                    bs.set(j);
                 }
             }
         }
         setupBuffer(ranges);
     }
-    
+
     @Override
     public int getType() {
-        return CCLASS;      
+        return CCLASS;
     }
-    
+
     @Override
     public String getName() {
         return "Character Class";
     }
-    
+
     @Override
     public boolean equals(Object other) {
         if (!(other instanceof CClassNode)) return false;
         CClassNode cc = (CClassNode)other;
         return ctype == cc.ctype && isNot() == cc.isNot() && enc == cc.enc;
     }
-    
+
     @Override
     public int hashCode() {
         if (Config.USE_SHARED_CCLASS_TABLE) {
@@ -107,51 +114,51 @@ public final class CClassNode extends Node {
             return super.hashCode();
         }
     }
-    
+
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder();
         value.append("\n  flags: " + flagsToString());
         value.append("\n  bs: " + pad(bs, level + 1));
         value.append("\n  mbuf: " + pad(mbuf, level + 1));
-        
+
         return value.toString();
-    }   
-    
+    }
+
     public String flagsToString() {
         StringBuilder flags = new StringBuilder();
         if (isNot()) flags.append("NOT ");
         if (isShare()) flags.append("SHARE ");
         return flags.toString();
     }
-    
-    private void setupBuffer(int[]ranges) { 
+
+    private void setupBuffer(int[]ranges) {
         if (ranges != null) {
             if (ranges[0] == 0) return;
             mbuf = new CodeRangeBuffer(ranges);
         }
     }
-    
+
     public boolean isEmpty() {
         return mbuf == null && bs.isEmpty();
     }
-    
+
     public void addCodeRangeToBuf(int from, int to) {
         mbuf = CodeRangeBuffer.addCodeRangeToBuff(mbuf, from, to);
     }
-    
+
     public void addCodeRange(ScanEnvironment env, int from, int to) {
         mbuf = CodeRangeBuffer.addCodeRange(mbuf, env, from, to);
     }
-    
+
     public void addAllMultiByteRange(Encoding enc) {
         mbuf = CodeRangeBuffer.addAllMultiByteRange(enc, mbuf);
     }
-    
+
     public void clearNotFlag(Encoding enc) {
         if (isNot()) {
             bs.invert();
-            
+
             if (!enc.isSingleByte()) {
                 mbuf = CodeRangeBuffer.notCodeRangeBuff(enc, mbuf);
             }
@@ -173,41 +180,41 @@ public final class CClassNode extends Node {
             bsr1.invertTo(bs1);
             bsr1 = bs1;
         }
-        
+
         if (not2) {
             BitSet bs2 = new BitSet();
             bsr2.invertTo(bs2);
             bsr2 = bs2;
         }
-        
+
         bsr1.and(bsr2);
-        
+
         if (bsr1 != bs) {
             bs.copy(bsr1);
             bsr1 = bs;
         }
-        
+
         if (not1) {
             bs.invert();
         }
-        
+
         CodeRangeBuffer pbuf = null;
-        
+
         if (!enc.isSingleByte()) {
-            if (not1 && not2) {            
+            if (not1 && not2) {
                 pbuf = CodeRangeBuffer.orCodeRangeBuff(enc, buf1, false, buf2, false);
             } else {
                 pbuf = CodeRangeBuffer.andCodeRangeBuff(buf1, not1, buf2, not2);
-                
+
                 if (not1) {
                     pbuf = CodeRangeBuffer.notCodeRangeBuff(enc, pbuf);
                 }
             }
             mbuf = pbuf;
         }
-        
+
     }
-    
+
     // or_cclass
     public void or(CClassNode other, Encoding enc) {
         boolean not1 = isNot();
@@ -216,30 +223,30 @@ public final class CClassNode extends Node {
         boolean not2 = other.isNot();
         BitSet bsr2 = other.bs;
         CodeRangeBuffer buf2 = other.mbuf;
-        
+
         if (not1) {
             BitSet bs1 = new BitSet();
             bsr1.invertTo(bs1);
             bsr1 = bs1;
         }
-        
+
         if (not2) {
             BitSet bs2 = new BitSet();
             bsr2.invertTo(bs2);
             bsr2 = bs2;
         }
-        
+
         bsr1.or(bsr2);
-        
+
         if (bsr1 != bs) {
             bs.copy(bsr1);
             bsr1 = bs;
         }
-        
+
         if (not1) {
             bs.invert();
         }
-        
+
         if (!enc.isSingleByte()) {
             CodeRangeBuffer pbuf = null;
             if (not1 && not2) {
@@ -247,13 +254,13 @@ public final class CClassNode extends Node {
             } else {
                 pbuf = CodeRangeBuffer.orCodeRangeBuff(enc, buf1, not1, buf2, not2);
                 if (not1) {
-                    pbuf = CodeRangeBuffer.notCodeRangeBuff(enc, pbuf); 
+                    pbuf = CodeRangeBuffer.notCodeRangeBuff(enc, pbuf);
                 }
             }
             mbuf = pbuf;
         }
     }
-    
+
     // add_ctype_to_cc_by_range // Encoding out!
     public void addCTypeByRange(int ctype, boolean not, Encoding enc, int sbOut, int mbr[]) {
         int n = mbr[0];
@@ -262,14 +269,21 @@ public final class CClassNode extends Node {
             for (int i=0; i<n; i++) {
                 for (int j=mbr[i * 2 + 1]; j<=mbr[i * 2 + 2]; j++) {
                     if (j >= sbOut) {
-                        if (j == mbr[i * 2 + 2]) {
-                            i++;
-                        } else if (j > mbr[i * 2 + 1]) { 
-                            addCodeRangeToBuf(j, mbr[i * 2 + 2]);
-                            i++;
+                        if (Config.VANILLA) {
+                            if (j == mbr[i * 2 + 2]) {
+                                i++;
+                            } else if (j > mbr[i * 2 + 1]) {
+                                addCodeRangeToBuf(j, mbr[i * 2 + 2]);
+                                i++;
+                            }
+                        } else {
+                            if (j >= mbr[i * 2 + 1]) {
+                                addCodeRangeToBuf(j, mbr[i * 2 + 2]);
+                                i++;
+                            }
                         }
                         // !goto sb_end!, remove duplication!
-                        for (; i<n; i++) {           
+                        for (; i<n; i++) {
                             addCodeRangeToBuf(mbr[2 * i + 1], mbr[2 * i + 2]);
                         }
                         return;
@@ -278,13 +292,13 @@ public final class CClassNode extends Node {
                 }
             }
             // !sb_end:!
-            for (int i=0; i<n; i++) {           
+            for (int i=0; i<n; i++) {
                 addCodeRangeToBuf(mbr[2 * i + 1], mbr[2 * i + 2]);
             }
-            
+
         } else {
             int prev = 0;
-            
+
             for (int i=0; i<n; i++) {
                 for (int j=prev; j < mbr[2 * i + 1]; j++) {
                     if (j >= sbOut) {
@@ -305,7 +319,7 @@ public final class CClassNode extends Node {
             for (int j=prev; j<sbOut; j++) {
                 bs.set(j);
             }
-            
+
             // !sb_end2:!
             prev = sbOut;
             for (int i=0; i<n; i++) {
@@ -315,12 +329,33 @@ public final class CClassNode extends Node {
             if (prev < 0x7fffffff/*!!!*/) addCodeRangeToBuf(prev, 0x7fffffff);
         }
     }
-    
+
     public void addCType(int ctype, boolean not, ScanEnvironment env, IntHolder sbOut) {
         Encoding enc = env.enc;
 
-        int[]ranges = enc.ctypeCodeRange(ctype, sbOut);
+        if (Config.NON_UNICODE_SDW) {
+            switch(ctype) {
+            case CharacterType.D:
+            case CharacterType.S:
+            case CharacterType.W:
+                ctype ^= CharacterType.SPECIAL_MASK;
+                if (not) {
+                    for (int c = 0; c < BitSet.SINGLE_BYTE_SIZE; c++) {
+                        if (!ASCIIEncoding.INSTANCE.isCodeCType(c, ctype)) bs.set(c);
+                        //if ((AsciiTables.AsciiCtypeTable[c] & (1 << ctype)) == 0) bs.set(c);
+                    }
+                    addAllMultiByteRange(enc);
+                } else {
+                    for (int c = 0; c < BitSet.SINGLE_BYTE_SIZE; c++) {
+                        if (ASCIIEncoding.INSTANCE.isCodeCType(c, ctype)) bs.set(c);
+                        //if ((AsciiTables.AsciiCtypeTable[c] & (1 << ctype)) != 0) bs.set(c);
+                    }
+                }
+                return;
+            }
+        }
 
+        int[]ranges = enc.ctypeCodeRange(ctype, sbOut);
         if (ranges != null) {
             addCTypeByRange(ctype, not, enc, sbOut.value, ranges);
             return;
@@ -349,7 +384,7 @@ public final class CClassNode extends Node {
                 }
             }
             break;
-        
+
         case CharacterType.GRAPH:
         case CharacterType.PRINT:
             if (not) {
@@ -363,7 +398,7 @@ public final class CClassNode extends Node {
                 addAllMultiByteRange(enc);
             }
             break;
-        
+
         case CharacterType.WORD:
             if (!not) {
                 for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
@@ -375,17 +410,17 @@ public final class CClassNode extends Node {
                 for (int c=0; c<BitSet.SINGLE_BYTE_SIZE; c++) {
                     try {
                         if (enc.codeToMbcLength(c) > 0 && /* check invalid code point */
-                                !enc.isWord(c)) bs.set(c);    
+                                !enc.isWord(c)) bs.set(c);
                     } catch (EncodingException ve) {};
                 }
             }
             break;
-        
+
         default:
             throw new InternalException(ErrorMessages.ERR_PARSER_BUG);
         } // switch
     }
-    
+
     public static final class CCStateArg {
         public int v;
         public int vs;
@@ -395,12 +430,12 @@ public final class CClassNode extends Node {
         public CCVALTYPE type;
         public CCSTATE state;
     }
-    
+
     public void nextStateClass(CCStateArg arg, ScanEnvironment env) {
         if (arg.state == CCSTATE.RANGE) throw new SyntaxException(ErrorMessages.ERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE);
-        
+
         if (arg.state == CCSTATE.VALUE && arg.type != CCVALTYPE.CLASS) {
-            if (arg.type == CCVALTYPE.SB) { 
+            if (arg.type == CCVALTYPE.SB) {
                 bs.set(arg.vs);
             } else if (arg.type == CCVALTYPE.CODE_POINT) {
                 addCodeRange(env, arg.vs, arg.vs);
@@ -409,24 +444,24 @@ public final class CClassNode extends Node {
         arg.state = CCSTATE.VALUE;
         arg.type = CCVALTYPE.CLASS;
     }
-    
+
     public void nextStateValue(CCStateArg arg, ScanEnvironment env) {
 
         switch(arg.state) {
         case VALUE:
             if (arg.type == CCVALTYPE.SB) {
                 if (arg.vs > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
-                bs.set(arg.vs);             
+                bs.set(arg.vs);
             } else if (arg.type == CCVALTYPE.CODE_POINT) {
                 addCodeRange(env, arg.vs, arg.vs);
             }
             break;
-            
+
         case RANGE:
             if (arg.inType == arg.type) {
                 if (arg.inType == CCVALTYPE.SB) {
                     if (arg.vs > 0xff || arg.v > 0xff) throw new ValueException(ErrorMessages.ERR_INVALID_CODE_POINT_VALUE);
-                    
+
                     if (arg.vs > arg.v) {
                         if (env.syntax.allowEmptyRangeInCC()) {
                             // goto ccs_range_end
@@ -448,7 +483,7 @@ public final class CClassNode extends Node {
                         break;
                     } else {
                         throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
-                    }                   
+                    }
                 }
                 bs.setRange(arg.vs, arg.v < 0xff ? arg.v : 0xff);
                 addCodeRange(env, arg.vs, arg.v);
@@ -456,26 +491,26 @@ public final class CClassNode extends Node {
             // ccs_range_end:
             arg.state = CCSTATE.COMPLETE;
             break;
-            
+
         case COMPLETE:
         case START:
             arg.state = CCSTATE.VALUE;
             break;
-            
+
         default:
             break;
 
         } // switch
-            
+
         arg.vsIsRaw = arg.vIsRaw;
         arg.vs = arg.v;
         arg.type = arg.inType;
     }
-    
+
     // onig_is_code_in_cc_len
     public boolean isCodeInCCLength(int encLength, int code) {
         boolean found;
-        
+
         if (encLength > 1 || code >= BitSet.SINGLE_BYTE_SIZE) {
             if (mbuf == null) {
                 found = false;
@@ -485,7 +520,7 @@ public final class CClassNode extends Node {
         } else {
             found = bs.at(code);
         }
-        
+
         if (isNot()) {
             return !found;
         } else {
@@ -493,7 +528,7 @@ public final class CClassNode extends Node {
         }
     }
 
-    // onig_is_code_in_cc   
+    // onig_is_code_in_cc
     public boolean isCodeInCC(Encoding enc, int code) {
         int len;
         if (enc.minLength() > 1) {
@@ -501,31 +536,31 @@ public final class CClassNode extends Node {
         } else {
             len = enc.codeToMbcLength(code);
         }
-        return isCodeInCCLength(len, code); 
+        return isCodeInCCLength(len, code);
     }
-    
+
     public void setNot() {
         flags |= FLAG_NCCLASS_NOT;
     }
-    
+
     public void clearNot() {
         flags &= ~FLAG_NCCLASS_NOT;
     }
-    
+
     public boolean isNot() {
         return (flags & FLAG_NCCLASS_NOT) != 0;
     }
-    
+
     public void setShare() {
         flags |= FLAG_NCCLASS_SHARE;
     }
-    
+
     public void clearShare() {
         flags &= ~FLAG_NCCLASS_SHARE;
     }
-    
+
     public boolean isShare() {
         return (flags & FLAG_NCCLASS_SHARE) != 0;
     }
-    
+
 }
diff --git a/src/org/joni/ast/CTypeNode.java b/src/org/joni/ast/CTypeNode.java
index 093216a..017ce54 100644
--- a/src/org/joni/ast/CTypeNode.java
+++ b/src/org/joni/ast/CTypeNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -22,29 +22,29 @@ package org.joni.ast;
 public final class CTypeNode extends Node {
     public int ctype;
     public boolean not;
-    
+
     public CTypeNode(int type, boolean not) {
         this.ctype= type;
         this.not = not;
     }
-    
+
     @Override
     public int getType() {
         return CTYPE;
     }
-    
+
     @Override
     public String getName() {
         return "Character Type";
-    }   
-    
+    }
+
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder();
         value.append("\n  ctype: " + ctype);
         value.append("\n  not: " + not);
-        
+
         return value.toString();
     }
-    
+
 }
diff --git a/src/org/joni/ast/CallNode.java b/src/org/joni/ast/CallNode.java
index 8261f75..a361544 100644
--- a/src/org/joni/ast/CallNode.java
+++ b/src/org/joni/ast/CallNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -32,24 +32,24 @@ public final class CallNode extends StateNode {
     public int groupNum;
     public Node target;             // is it an EncloseNode always ?
     public UnsetAddrList unsetAddrList;
-    
+
     public CallNode(byte[]name, int nameP, int nameEnd, int gnum) {
         this.name = name;
         this.nameP = nameP;
         this.nameEnd = nameEnd;
         this.groupNum = gnum; /* call by number if gnum != 0 */
     }
-    
+
     @Override
     public int getType() {
         return CALL;
     }
-    
+
     @Override
     protected void setChild(Node newChild) {
         target = newChild;
     }
-    
+
     @Override
     protected Node getChild() {
         return target;
@@ -57,21 +57,21 @@ public final class CallNode extends StateNode {
 
     public void setTarget(Node tgt) {
         target = tgt;
-        tgt.parent = this; 
-    }    
+        tgt.parent = this;
+    }
 
     @Override
     public String getName() {
         return "Call";
     }
-    
+
     @Override
     public void verifyTree(Set<Node> set, WarnCallback warnings) {
         if (target == null || target.parent == this)
             warnings.warn(this.getAddressName() + " doesn't point to a target or the target has been stolen");
         // do not recurse here
-    }    
-    
+    }
+
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder(super.toString(level));
@@ -79,8 +79,8 @@ public final class CallNode extends StateNode {
         value.append("\n  groupNum: " + groupNum);
         value.append("\n  target: " + pad(target.getAddressName(), level + 1));
         value.append("\n  unsetAddrList: " + pad(unsetAddrList, level + 1));
-        
+
         return value.toString();
-    }   
-    
+    }
+
 }
diff --git a/src/org/joni/ast/ConsAltNode.java b/src/org/joni/ast/ConsAltNode.java
index 3d7f784..7d456fe 100644
--- a/src/org/joni/ast/ConsAltNode.java
+++ b/src/org/joni/ast/ConsAltNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -30,27 +30,27 @@ public final class ConsAltNode extends Node {
     public Node car;
     public ConsAltNode cdr;
     private int type;           // List or Alt
-    
+
     private ConsAltNode(Node car, ConsAltNode cdr, int type) {
         this.car = car;
         if (car != null) car.parent = this;
         this.cdr = cdr;
         if (cdr != null) cdr.parent = this;
-        
+
         this.type = type;
     }
-    
+
     public static ConsAltNode newAltNode(Node left, ConsAltNode right) {
         return new ConsAltNode(left, right, ALT);
     }
-    
+
     public static ConsAltNode newListNode(Node left, ConsAltNode right) {
         return new ConsAltNode(left, right, LIST);
     }
-    
+
     public static ConsAltNode listAdd(ConsAltNode list, Node x) {
         ConsAltNode n = newListNode(x, null);
-        
+
         if (list != null) {
             while (list.cdr != null) {
                 list = list.cdr;
@@ -59,25 +59,25 @@ public final class ConsAltNode extends Node {
         }
         return n;
     }
-    
+
     public void toListNode() {
         type = LIST;
     }
-    
+
     public void toAltNode() {
         type = ALT;
     }
-    
+
     @Override
-    public int getType() {      
+    public int getType() {
         return type;
     }
-    
+
     @Override
     protected void setChild(Node newChild) {
         car = newChild;
-    }    
-    
+    }
+
     @Override
     protected Node getChild() {
         return car;
@@ -95,10 +95,9 @@ public final class ConsAltNode extends Node {
                 withCan.cdr = tmp;
             }
         }
-        
         super.swap(with);
-    } 
-    
+    }
+
     @Override
     public void verifyTree(Set<Node> set, WarnCallback warnings) {
         if (!set.contains(this)) {
@@ -123,13 +122,13 @@ public final class ConsAltNode extends Node {
         ca.parent = this;
         return car;
     }
-    
+
     public ConsAltNode setCdr(ConsAltNode cd) {
         cdr = cd;
         cd.parent = this;
         return cdr;
     }
-    
+
     @Override
     public String getName() {
         switch (type) {
@@ -145,10 +144,10 @@ public final class ConsAltNode extends Node {
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder();
-        value.append("\n  left: " + pad(car, level + 1));
-        value.append("\n  right: " + (cdr == null ? "NULL" : cdr.toString()));
-        
+        value.append("\n  car: " + pad(car, level + 1));
+        value.append("\n  cdr: " + (cdr == null ? "NULL" : cdr.toString()));
+
         return value.toString();
-    }   
+    }
 
 }
diff --git a/src/org/joni/ast/EncloseNode.java b/src/org/joni/ast/EncloseNode.java
index 02b2391..0a07ed1 100644
--- a/src/org/joni/ast/EncloseNode.java
+++ b/src/org/joni/ast/EncloseNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -24,7 +24,7 @@ import org.joni.Option;
 import org.joni.constants.EncloseType;
 
 public final class EncloseNode extends StateNode implements EncloseType {
-    
+
     public int type;                // enclose type
     public int regNum;
     public int option;
@@ -34,20 +34,20 @@ public final class EncloseNode extends StateNode implements EncloseType {
     public int maxLength;           // OnigDistance
     public int charLength;
     public int optCount;            // referenced count in optimize_node_left()
-    
+
     // node_new_enclose / onig_node_new_enclose
     public EncloseNode(int type) {
         this.type = type;
         callAddr = -1;
     }
-    
+
     // node_new_enclose_memory
     public EncloseNode(int option, boolean isNamed) {
         this(MEMORY);
         if (isNamed) setNamedGroup();
         if (Config.USE_SUBEXP_CALL) this.option = option;
     }
-    
+
     // node_new_option
     public EncloseNode(int option, int _) {
         this(OPTION);
@@ -58,12 +58,12 @@ public final class EncloseNode extends StateNode implements EncloseType {
     public int getType() {
         return ENCLOSE;
     }
-    
+
     @Override
     protected void setChild(Node newChild) {
         target = newChild;
     }
-    
+
     @Override
     protected Node getChild() {
         return target;
@@ -71,14 +71,14 @@ public final class EncloseNode extends StateNode implements EncloseType {
 
     public void setTarget(Node tgt) {
         target = tgt;
-        tgt.parent = this; 
-    }     
-    
+        tgt.parent = this;
+    }
+
     @Override
     public String getName() {
         return "Enclose";
-    }   
-    
+    }
+
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder(super.toString(level));
@@ -91,61 +91,61 @@ public final class EncloseNode extends StateNode implements EncloseType {
         value.append("\n  maxLength: " + maxLength);
         value.append("\n  charLength: " + charLength);
         value.append("\n  optCount: " + optCount);
-        
+
         return value.toString();
     }
-    
+
     public String typeToString() {
         StringBuilder types = new StringBuilder();
         if (isStopBacktrack()) types.append("STOP_BACKTRACK ");
         if (isMemory()) types.append("MEMORY ");
         if (isOption()) types.append("OPTION ");
-        
+
         return types.toString();
     }
-    
+
     public void setEncloseStatus(int flag) {
         state |= flag;
     }
-    
+
     public void clearEncloseStatus(int flag) {
         state &= ~flag;
     }
-    
+
     public void clearMemory() {
         type &= ~MEMORY;
     }
-    
+
     public void setMemory() {
         type |= MEMORY;
     }
-    
+
     public boolean isMemory() {
         return (type & MEMORY) != 0;
     }
-    
+
     public void clearOption() {
         type &= ~OPTION;
     }
-    
+
     public void setOption() {
         type |= OPTION;
     }
-    
+
     public boolean isOption() {
         return (type & OPTION) != 0;
     }
-    
+
     public void clearStopBacktrack() {
         type &= ~STOP_BACKTRACK;
     }
-    
+
     public void setStopBacktrack() {
         type |= STOP_BACKTRACK;
     }
-    
+
     public boolean isStopBacktrack() {
         return (type & STOP_BACKTRACK) != 0;
-    }   
-    
+    }
+
 }
diff --git a/src/org/joni/ast/Node.java b/src/org/joni/ast/Node.java
index eef7790..5a75221 100644
--- a/src/org/joni/ast/Node.java
+++ b/src/org/joni/ast/Node.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -27,13 +27,13 @@ import org.joni.constants.NodeType;
 
 public abstract class Node implements NodeType {
     public Node parent;
-    
+
     public abstract int getType();
 
     public final int getType2Bit() {
-        return 1 << getType(); 
-    }    
-    
+        return 1 << getType();
+    }
+
     protected void setChild(Node tgt){}         // default definition
     protected Node getChild(){return null;};    // default definition
 
@@ -42,20 +42,20 @@ public abstract class Node implements NodeType {
 
         //if (getChild() != null) getChild().parent = with;
         //if (with.getChild() != null) with.getChild().parent = this;
-        
+
         //tmp = getChild();
         //setChild(with.getChild());
         //with.setChild(tmp);
-        
+
         if (parent != null) parent.setChild(with);
-        
+
         if (with.parent != null) with.parent.setChild(this);
 
         tmp = parent;
         parent = with.parent;
         with.parent = tmp;
     }
-    
+
     // overridden by ConsAltNode and CallNode
     public void verifyTree(Set<Node> set, WarnCallback warnings) {
         if (!set.contains(this) && getChild() != null) {
@@ -65,31 +65,30 @@ public abstract class Node implements NodeType {
             }
             getChild().verifyTree(set, warnings);
         }
-    }     
-    
+    }
+
     public abstract String getName();
     protected abstract String toString(int level);
-    
+
     public String getAddressName() {
         return getName() + ":0x" + Integer.toHexString(System.identityHashCode(this));
     }
-    
+
     public final String toString() {
         StringBuilder s = new StringBuilder();
-        s.append("<" + getAddressName() + ">");
-        s.append("\n  parent: " + (parent == null ? "NULL" : parent.getAddressName())); 
+        s.append("<" + getAddressName() + " (" + (parent == null ? "NULL" : parent.getAddressName())  + ")>");
         return s + toString(0);
     }
-    
+
     protected static String pad(Object value, int level) {
         if (value == null) return "NULL";
-        
+
         StringBuilder pad = new StringBuilder("  ");
         for (int i=0; i<level; i++) pad.append(pad);
-        
+
         return value.toString().replace("\n",  "\n" + pad);
     }
-    
+
     public final boolean isInvalidQuantifier() {
         if (!Config.VANILLA) return false;
 
@@ -99,37 +98,37 @@ public abstract class Node implements NodeType {
 
         case ANCHOR:
             return true;
-            
+
         case ENCLOSE:
             /* allow enclosed elements */
             /* return is_invalid_quantifier_target(NENCLOSE(node)->target); */
             break;
-            
+
         case LIST:
             node = (ConsAltNode)this;
             do {
                 if (!node.car.isInvalidQuantifier()) return false;
             } while ((node = node.cdr) != null);
             return false;
-            
+
         case ALT:
             node = (ConsAltNode)this;
             do {
                 if (node.car.isInvalidQuantifier()) return true;
             } while ((node = node.cdr) != null);
             break;
-            
+
         default:
             break;
         }
-        
+
         return false;
     }
-    
+
     public final boolean isAllowedInLookBehind() {
-        return (getType2Bit() & ALLOWED_IN_LB) != 0; 
+        return (getType2Bit() & ALLOWED_IN_LB) != 0;
     }
-    
+
     public final boolean isSimple() {
         return (getType2Bit() & SIMPLE) != 0;
     }
diff --git a/src/org/joni/ast/QuantifierNode.java b/src/org/joni/ast/QuantifierNode.java
index 280f9bf..8ec53cb 100644
--- a/src/org/joni/ast/QuantifierNode.java
+++ b/src/org/joni/ast/QuantifierNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -30,52 +30,52 @@ public final class QuantifierNode extends StateNode {
     public int lower;
     public int upper;
     public boolean greedy;
-    
+
     public int targetEmptyInfo;
-    
+
     public Node headExact;
     public Node nextHeadExact;
     public boolean isRefered;           /* include called node. don't eliminate even if {0} */
-    
+
     // USE_COMBINATION_EXPLOSION_CHECK
-    public int  combExpCheckNum;        /* 1,2,3...: check,  0: no check  */ 
-    
+    public int  combExpCheckNum;        /* 1,2,3...: check,  0: no check  */
+
     public QuantifierNode(int lower, int upper, boolean byNumber) {
         this.lower = lower;
         this.upper = upper;
         greedy = true;
         targetEmptyInfo = TargetInfo.ISNOT_EMPTY;
-        
-        if (byNumber) setByNumber(); 
+
+        if (byNumber) setByNumber();
     }
 
     @Override
     public int getType() {
         return QTFR;
     }
-    
+
     @Override
     protected void setChild(Node newChild) {
         target = newChild;
     }
-    
+
     @Override
     protected Node getChild() {
         return target;
     }
-    
+
     public void setTarget(Node tgt) {
         target = tgt;
-        tgt.parent = this; 
+        tgt.parent = this;
     }
-    
-    public StringNode convertToString() {
+
+    public StringNode convertToString(int flag) {
         StringNode sn = new StringNode();
-        sn.flag = ((StringNode)target).flag;
+        sn.flag = flag;
         sn.swap(this);
         return sn;
     }
-    
+
     @Override
     public String getName() {
         return "Quantifier";
@@ -93,14 +93,14 @@ public final class QuantifierNode extends StateNode {
         value.append("\n  nextHeadExact: " + pad(nextHeadExact, level + 1));
         value.append("\n  isRefered: " + isRefered);
         value.append("\n  combExpCheckNum: " + combExpCheckNum);
-        
+
         return value.toString();
     }
-    
+
     public boolean isAnyCharStar() {
         return greedy && isRepeatInfinite(upper) && target.getType() == CANY;
     }
-    
+
     /* ?:0, *:1, +:2, ??:3, *?:4, +?:5 */
     protected int popularNum() {
         if (greedy) {
@@ -120,7 +120,7 @@ public final class QuantifierNode extends StateNode {
         }
         return -1;
     }
-    
+
     protected void set(QuantifierNode other) {
         setTarget(other.target);
         other.target = null;
@@ -128,7 +128,7 @@ public final class QuantifierNode extends StateNode {
         upper = other.upper;
         greedy = other.greedy;
         targetEmptyInfo = other.targetEmptyInfo;
-        
+
         //setHeadExact(other.headExact);
         //setNextHeadExact(other.nextHeadExact);
         headExact = other.headExact;
@@ -136,41 +136,41 @@ public final class QuantifierNode extends StateNode {
         isRefered = other.isRefered;
         combExpCheckNum = other.combExpCheckNum;
     }
-    
+
     public void reduceNestedQuantifier(QuantifierNode other) {
         int pnum = popularNum();
         int cnum = other.popularNum();
-        
+
         if (pnum < 0 || cnum < 0) return;
-        
+
         switch(Reduce.REDUCE_TABLE[cnum][pnum]) {
         case DEL:
-            // no need to set the parent here... 
+            // no need to set the parent here...
             // swap ?
             set(other); // *pnode = *cnode; ???
             break;
-            
+
         case A:
             setTarget(other.target);
             lower = 0;
             upper = REPEAT_INFINITE;
             greedy = true;
             break;
-            
+
         case AQ:
             setTarget(other.target);
             lower = 0;
             upper = REPEAT_INFINITE;
             greedy = false;
             break;
-            
+
         case QQ:
             setTarget(other.target);
             lower = 0;
             upper = 1;
             greedy = false;
             break;
-            
+
         case P_QQ:
             setTarget(other);
             lower = 0;
@@ -180,7 +180,7 @@ public final class QuantifierNode extends StateNode {
             other.upper = REPEAT_INFINITE;
             other.greedy = true;
             return;
-            
+
         case PQ_Q:
             setTarget(other);
             lower = 0;
@@ -190,7 +190,7 @@ public final class QuantifierNode extends StateNode {
             other.upper = REPEAT_INFINITE;
             other.greedy = false;
             return;
-            
+
         case ASIS:
             setTarget(other);
             return;
@@ -198,12 +198,12 @@ public final class QuantifierNode extends StateNode {
         // ??? remove the parent from target ???
         other.target = null; // remove target from reduced quantifier
     }
-    
+
     public int setQuantifier(Node tgt, boolean group, ScanEnvironment env, byte[]bytes, int p, int end) {
         if (lower == 1 && upper == 1) return 1;
-        
+
         switch(tgt.getType()) {
-        
+
         case STR:
             if (!group) {
                 StringNode sn = (StringNode)tgt;
@@ -216,25 +216,25 @@ public final class QuantifierNode extends StateNode {
                 }
             }
             break;
-            
+
         case QTFR:
             /* check redundant double repeat. */
             /* verbose warn (?:.?)? etc... but not warn (.?)? etc... */
             QuantifierNode qnt = (QuantifierNode)tgt;
             int nestQNum = popularNum();
             int targetQNum = qnt.popularNum();
-            
+
             if (Config.USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR) {
                 if (!isByNumber() && !qnt.isByNumber() && env.syntax.warnReduntantNestedRepeat()) {
                     switch(Reduce.REDUCE_TABLE[targetQNum][nestQNum]) {
                     case ASIS:
                         break;
-                        
+
                     case DEL:
-                        env.reg.warnings.warn(new String(bytes, p, end) + 
+                        env.reg.warnings.warn(new String(bytes, p, end) +
                                 " redundant nested repeat operator");
                         break;
-                        
+
                     default:
                         env.reg.warnings.warn(new String(bytes, p, end) +
                                 " nested repeat operator " + Reduce.PopularQStr[targetQNum] +
@@ -243,7 +243,7 @@ public final class QuantifierNode extends StateNode {
                     }
                 }
             } // USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR
-            
+
             if (targetQNum >= 0) {
                 if (nestQNum >= 0) {
                     reduceNestedQuantifier(qnt);
@@ -255,11 +255,11 @@ public final class QuantifierNode extends StateNode {
                     }
                 }
             }
-            
+
         default:
             break;
         }
-        
+
         setTarget(tgt);
         return 0;
     }
@@ -268,5 +268,5 @@ public final class QuantifierNode extends StateNode {
     public static boolean isRepeatInfinite(int n) {
         return n == REPEAT_INFINITE;
     }
-    
+
 }
diff --git a/src/org/joni/ast/StateNode.java b/src/org/joni/ast/StateNode.java
index 117d3df..299b319 100644
--- a/src/org/joni/ast/StateNode.java
+++ b/src/org/joni/ast/StateNode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
@@ -28,7 +28,7 @@ public abstract class StateNode extends Node implements NodeStatus {
     public String toString(int level) {
         return "\n  state: " + stateToString();
     }
-    
+
     public String stateToString() {
         StringBuilder states = new StringBuilder();
         if (isMinFixed()) states.append("MIN_FIXED ");
@@ -48,185 +48,185 @@ public abstract class StateNode extends Node implements NodeStatus {
 
         return states.toString();
     }
-    
+
     public boolean isMinFixed() {
         return (state & NST_MIN_FIXED) != 0;
     }
-    
+
     public void setMinFixed() {
         state |= NST_MIN_FIXED;
     }
-    
+
     public void clearMinFixed() {
         state &= ~NST_MIN_FIXED;
     }
-    
+
     public boolean isMaxFixed() {
         return (state & NST_MAX_FIXED) != 0;
     }
-    
+
     public void setMaxFixed() {
         state |= NST_MAX_FIXED;
     }
-    
+
     public void clearMaxFixed() {
-        state &= ~NST_MAX_FIXED;        
+        state &= ~NST_MAX_FIXED;
     }
-    
+
     public boolean isCLenFixed() {
         return (state & NST_CLEN_FIXED) != 0;
     }
-    
+
     public void setCLenFixed() {
         state |= NST_CLEN_FIXED;
     }
-    
+
     public void clearCLenFixed() {
-        state &= ~NST_CLEN_FIXED;       
+        state &= ~NST_CLEN_FIXED;
     }
-    
+
     public boolean isMark1() {
         return (state & NST_MARK1) != 0;
     }
-    
+
     public void setMark1() {
         state |= NST_MARK1;
     }
-    
+
     public void clearMark1() {
-        state &= ~NST_MARK1;        
+        state &= ~NST_MARK1;
     }
-    
+
     public boolean isMark2() {
         return (state & NST_MARK2) != 0;
     }
-    
+
     public void setMark2() {
         state |= NST_MARK2;
     }
-    
+
     public void clearMark2() {
-        state &= ~NST_MARK2;        
+        state &= ~NST_MARK2;
     }
-    
+
     public boolean isMemBackrefed() {
         return (state & NST_MEM_BACKREFED) != 0;
     }
-    
+
     public void setMemBackrefed() {
         state |= NST_MEM_BACKREFED;
     }
-    
+
     public void clearMemBackrefed() {
-        state &= ~NST_MEM_BACKREFED;        
+        state &= ~NST_MEM_BACKREFED;
     }
-    
+
     public boolean isStopBtSimpleRepeat() {
         return (state & NST_STOP_BT_SIMPLE_REPEAT) != 0;
     }
-    
+
     public void setStopBtSimpleRepeat() {
         state |= NST_STOP_BT_SIMPLE_REPEAT;
     }
-    
+
     public void clearStopBtSimpleRepeat() {
-        state &= ~NST_STOP_BT_SIMPLE_REPEAT;        
+        state &= ~NST_STOP_BT_SIMPLE_REPEAT;
     }
 
     public boolean isRecursion() {
         return (state & NST_RECURSION) != 0;
     }
-    
+
     public void setRecursion() {
         state |= NST_RECURSION;
     }
-    
+
     public void clearRecursion() {
-        state &= ~NST_RECURSION;        
+        state &= ~NST_RECURSION;
     }
-    
+
     public boolean isCalled() {
         return (state & NST_CALLED) != 0;
     }
-    
+
     public void setCalled() {
         state |= NST_CALLED;
     }
-    
-    public void clearCAlled() {     
-        state &= ~NST_CALLED;       
+
+    public void clearCAlled() {
+        state &= ~NST_CALLED;
     }
-    
+
     public boolean isAddrFixed() {
         return (state & NST_ADDR_FIXED) != 0;
     }
-    
+
     public void setAddrFixed() {
         state |= NST_ADDR_FIXED;
     }
-    
-    public void clearAddrFixed() {      
-        state &= ~NST_ADDR_FIXED;       
+
+    public void clearAddrFixed() {
+        state &= ~NST_ADDR_FIXED;
     }
-    
+
     public boolean isNamedGroup() {
         return (state & NST_NAMED_GROUP) != 0;
     }
-    
+
     public void setNamedGroup() {
         state |= NST_NAMED_GROUP;
     }
-    
-    public void clearNamedGroup() {     
-        state &= ~NST_NAMED_GROUP;      
+
+    public void clearNamedGroup() {
+        state &= ~NST_NAMED_GROUP;
     }
 
     public boolean isNameRef() {
         return (state & NST_NAME_REF) != 0;
     }
-    
+
     public void setNameRef() {
         state |= NST_NAME_REF;
     }
-    
-    public void clearNameRef() {        
-        state &= ~NST_NAME_REF;     
+
+    public void clearNameRef() {
+        state &= ~NST_NAME_REF;
     }
-    
+
     public boolean isInRepeat() {
         return (state & NST_IN_REPEAT) != 0;
     }
-    
+
     public void setInRepeat() {
         state |= NST_IN_REPEAT;
     }
-    
-    public void clearInRepeat() {       
-        state &= ~NST_IN_REPEAT;        
+
+    public void clearInRepeat() {
+        state &= ~NST_IN_REPEAT;
     }
-    
+
     public boolean isNestLevel() {
         return (state & NST_NEST_LEVEL) != 0;
     }
-    
+
     public void setNestLevel() {
         state |= NST_NEST_LEVEL;
     }
-    
-    public void clearNestLevel() {      
-        state &= ~NST_NEST_LEVEL;       
+
+    public void clearNestLevel() {
+        state &= ~NST_NEST_LEVEL;
     }
-    
+
     public boolean isByNumber() {
         return (state & NST_BY_NUMBER) != 0;
     }
-    
+
     public void setByNumber() {
         state |= NST_BY_NUMBER;
     }
-    
-    public void clearByNumber() {       
-        state &= ~NST_BY_NUMBER;        
+
+    public void clearByNumber() {
+        state &= ~NST_BY_NUMBER;
     }
-    
+
 }
diff --git a/src/org/joni/ast/StringNode.java b/src/org/joni/ast/StringNode.java
index bf8217e..15ed04c 100644
--- a/src/org/joni/ast/StringNode.java
+++ b/src/org/joni/ast/StringNode.java
@@ -1,38 +1,40 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.ast;
 
 import org.jcodings.Encoding;
+import org.joni.Config;
 import org.joni.constants.StringType;
 
 public final class StringNode extends Node implements StringType {
-    
+
     private static final int NODE_STR_MARGIN = 16;
     private static final int NODE_STR_BUF_SIZE = 24;
-    
+    public static final StringNode EMPTY = new StringNode(null, Integer.MAX_VALUE, Integer.MAX_VALUE);
+
     public byte[]bytes;
     public int p;
     public int end;
-    
-    int flag;
-    
+
+    public int flag;
+
     public StringNode() {
         this.bytes = new byte[NODE_STR_BUF_SIZE];
     }
@@ -43,7 +45,7 @@ public final class StringNode extends Node implements StringType {
         this.end = end;
         setShared();
     }
-    
+
     public StringNode(byte c) {
         this();
         bytes[end++] = c;
@@ -60,12 +62,12 @@ public final class StringNode extends Node implements StringType {
             bytes = tmp;
         }
     }
-    
+
     /* COW and/or ensure there is ahead bytes available in node's buffer
      */
     private void modifyEnsure(int ahead) {
-        int len = (end - p) + ahead;
         if (isShared()) {
+            int len = (end - p) + ahead;
             byte[]tmp = new byte[len + NODE_STR_MARGIN];
             System.arraycopy(bytes, p, tmp, 0, end - p);
             bytes = tmp;
@@ -73,46 +75,43 @@ public final class StringNode extends Node implements StringType {
             p = 0;
             clearShared();
         } else {
-            if (len >= bytes.length) {
-                byte[]tmp = new byte[len + NODE_STR_MARGIN];
-                System.arraycopy(bytes, p, tmp, 0, end - p);
-                bytes = tmp;
-            }
+            ensure(ahead);
         }
     }
-    
+
     @Override
     public int getType() {
         return STR;
     }
-    
+
     @Override
     public String getName() {
         return "String";
-    }   
-    
+    }
+
     @Override
     public String toString(int level) {
         StringBuilder value = new StringBuilder();
-        value.append("\n  bytes: ");
+        value.append("\n  bytes: '");
         for (int i=p; i<end; i++) {
             if ((bytes[i] & 0xff) >= 0x20 && (bytes[i] & 0xff) < 0x7f) {
                 value.append((char)bytes[i]);
             } else {
-                value.append(String.format(" 0x%02x", bytes[i]));
+                value.append(String.format("[0x%02x]", bytes[i]));
             }
         }
+        value.append("'");
         return value.toString();
     }
 
     public int length() {
         return end - p;
     }
-    
+
     public int length(Encoding enc) {
         return enc.strLength(bytes, p, end);
     }
-    
+
     public StringNode splitLastChar(Encoding enc) {
         StringNode n = null;
 
@@ -126,7 +125,7 @@ public final class StringNode extends Node implements StringType {
         }
         return n;
     }
-    
+
     public boolean canBeSplit(Encoding enc) {
         if (end > p) {
             return enc.length(bytes, p, end) < (end - p);
@@ -140,19 +139,24 @@ public final class StringNode extends Node implements StringType {
         this.end = end;
         setShared();
     }
-    
+
     public void cat(byte[]cat, int catP, int catEnd) {
         int len = catEnd - catP;
         modifyEnsure(len);
         System.arraycopy(cat, catP, bytes, end, len);
         end += len;
     }
-    
+
     public void cat(byte c) {
         modifyEnsure(1);
         bytes[end++] = c;
     }
-    
+
+    public void catCode(int code, Encoding enc) {
+        ensure(Config.ENC_CODE_TO_MBC_MAXLEN);
+        end += enc.codeToMbc(code, bytes, end);
+    }
+
     public void clear() {
         if (bytes.length > NODE_STR_BUF_SIZE) bytes = new byte[NODE_STR_BUF_SIZE];
         flag = 0;
@@ -162,47 +166,47 @@ public final class StringNode extends Node implements StringType {
     public void setRaw() {
         flag |= NSTR_RAW;
     }
-    
+
     public void clearRaw() {
         flag &= ~NSTR_RAW;
     }
-    
+
     public boolean isRaw() {
         return (flag & NSTR_RAW) != 0;
     }
-    
+
     public void setAmbig() {
         flag |= NSTR_AMBIG;
     }
-    
+
     public void clearAmbig() {
         flag &= ~NSTR_AMBIG;
     }
-    
+
     public boolean isAmbig() {
         return (flag & NSTR_AMBIG) != 0;
     }
-    
+
     public void setDontGetOptInfo() {
         flag |= NSTR_DONT_GET_OPT_INFO;
     }
-    
+
     public void clearDontGetOptInfo() {
         flag &= ~NSTR_DONT_GET_OPT_INFO;
     }
-    
+
     public boolean isDontGetOptInfo() {
         return (flag & NSTR_DONT_GET_OPT_INFO) != 0;
     }
-    
+
     public void setShared() {
         flag |= NSTR_SHARED;
     }
-    
+
     public void clearShared() {
         flag &= ~NSTR_SHARED;
     }
-    
+
     public boolean isShared() {
         return (flag & NSTR_SHARED) != 0;
     }
diff --git a/src/org/joni/bench/AbstractBench.java b/src/org/joni/bench/AbstractBench.java
index 5e058f9..7abcb51 100644
--- a/src/org/joni/bench/AbstractBench.java
+++ b/src/org/joni/bench/AbstractBench.java
@@ -9,7 +9,7 @@ public abstract class AbstractBench {
     protected void bench(String _reg, String _str, int warmup, int times) throws Exception {
         byte[] reg = _reg.getBytes();
         byte[] str = _str.getBytes();
-        
+
         Regex p = new Regex(reg,0,reg.length,Option.DEFAULT,ASCIIEncoding.INSTANCE,Syntax.DEFAULT);
 
         System.err.println("::: /" + _reg + "/ =~ \"" + _str + "\", " + warmup + " * " + times + " times");
@@ -27,7 +27,7 @@ public abstract class AbstractBench {
     protected void benchBestOf(String _reg, String _str, int warmup, int times) throws Exception {
         byte[] reg = _reg.getBytes();
         byte[] str = _str.getBytes();
-        
+
         Regex p = new Regex(reg,0,reg.length,Option.DEFAULT,ASCIIEncoding.INSTANCE,Syntax.DEFAULT);
 
         System.err.println("::: /" + _reg + "/ =~ \"" + _str + "\", " + warmup + " * " + times + " times");
diff --git a/src/org/joni/constants/AnchorType.java b/src/org/joni/constants/AnchorType.java
index 144dd1d..5d7336d 100644
--- a/src/org/joni/constants/AnchorType.java
+++ b/src/org/joni/constants/AnchorType.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
@@ -38,21 +38,21 @@ public interface AnchorType {
 
     final int ANYCHAR_STAR      = (1<<14);   /* ".*" optimize info */
     final int ANYCHAR_STAR_ML   = (1<<15);   /* ".*" optimize info (multi-line) */
-    
+
     final int ANYCHAR_STAR_MASK = (ANYCHAR_STAR | ANYCHAR_STAR_ML);
     final int END_BUF_MASK      = (END_BUF | SEMI_END_BUF);
-    
+
     final int ALLOWED_IN_LB =     ( LOOK_BEHIND |
                                     BEGIN_LINE |
                                     END_LINE |
                                     BEGIN_BUF |
                                     BEGIN_POSITION );
-    
+
     final int ALLOWED_IN_LB_NOT = ( LOOK_BEHIND |
                                     LOOK_BEHIND_NOT |
                                     BEGIN_LINE |
                                     END_LINE |
                                     BEGIN_BUF |
-                                    BEGIN_POSITION ); 
-        
+                                    BEGIN_POSITION );
+
 }
diff --git a/src/org/joni/constants/Arguments.java b/src/org/joni/constants/Arguments.java
index 1aacfdd..6098239 100644
--- a/src/org/joni/constants/Arguments.java
+++ b/src/org/joni/constants/Arguments.java
@@ -1,27 +1,27 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
 
 public interface Arguments {
-    final int SPECIAL       = -1;   
-    final int NON           = 0;    
+    final int SPECIAL       = -1;
+    final int NON           = 0;
     final int RELADDR       = 1;
     final int ABSADDR       = 2;
     final int LENGTH        = 3;
diff --git a/src/org/joni/constants/AsmConstants.java b/src/org/joni/constants/AsmConstants.java
index 6329780..2e28958 100644
--- a/src/org/joni/constants/AsmConstants.java
+++ b/src/org/joni/constants/AsmConstants.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/CCSTATE.java b/src/org/joni/constants/CCSTATE.java
index 23baa87..669b821 100644
--- a/src/org/joni/constants/CCSTATE.java
+++ b/src/org/joni/constants/CCSTATE.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/CCVALTYPE.java b/src/org/joni/constants/CCVALTYPE.java
index b531e30..b2bcb30 100644
--- a/src/org/joni/constants/CCVALTYPE.java
+++ b/src/org/joni/constants/CCVALTYPE.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/EncloseType.java b/src/org/joni/constants/EncloseType.java
index 553b5dc..125af0c 100644
--- a/src/org/joni/constants/EncloseType.java
+++ b/src/org/joni/constants/EncloseType.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
@@ -23,7 +23,7 @@ public interface EncloseType {
     final int MEMORY                = 1<<0;
     final int OPTION                = 1<<1;
     final int STOP_BACKTRACK        = 1<<2;
-    
+
     final int ALLOWED_IN_LB         = MEMORY;
     final int ALLOWED_IN_LB_NOT     = 0;
 }
diff --git a/src/org/joni/constants/MetaChar.java b/src/org/joni/constants/MetaChar.java
index 3589aff..37f94a7 100644
--- a/src/org/joni/constants/MetaChar.java
+++ b/src/org/joni/constants/MetaChar.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
@@ -23,9 +23,9 @@ public interface MetaChar {
     final int ESCAPE            = 0;
     final int ANYCHAR           = 1;
     final int ANYTIME           = 2;
-    final int ZERO_OR_ONE_TIME  = 3;    
+    final int ZERO_OR_ONE_TIME  = 3;
     final int ONE_OR_MORE_TIME  = 4;
     final int ANYCHAR_ANYTIME   = 5;
-    
+
     final int INEFFECTIVE_META_CHAR = 0;
 }
diff --git a/src/org/joni/constants/NodeStatus.java b/src/org/joni/constants/NodeStatus.java
index 901d47d..e7a4754 100644
--- a/src/org/joni/constants/NodeStatus.java
+++ b/src/org/joni/constants/NodeStatus.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/NodeType.java b/src/org/joni/constants/NodeType.java
index dccece2..4e37d67 100644
--- a/src/org/joni/constants/NodeType.java
+++ b/src/org/joni/constants/NodeType.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
@@ -32,7 +32,7 @@ public interface NodeType {
     final int  LIST       = 8;
     final int  ALT        = 9;
     final int  CALL       = 10;
-    
+
     final int BIT_STR        = 1 << STR;
     final int BIT_CCLASS     = 1 << CCLASS;
     final int BIT_CTYPE      = 1 << CTYPE;
@@ -44,9 +44,9 @@ public interface NodeType {
     final int BIT_LIST       = 1 << LIST;
     final int BIT_ALT        = 1 << ALT;
     final int BIT_CALL       = 1 << CALL;
-    
+
     /* allowed node types in look-behind */
-    final int ALLOWED_IN_LB = ( BIT_LIST | 
+    final int ALLOWED_IN_LB = ( BIT_LIST |
                                 BIT_ALT |
                                 BIT_STR |
                                 BIT_CCLASS |
@@ -56,11 +56,11 @@ public interface NodeType {
                                 BIT_ENCLOSE |
                                 BIT_QTFR |
                                 BIT_CALL );
-    
+
     final int SIMPLE =        ( BIT_STR |
                                 BIT_CCLASS |
                                 BIT_CTYPE |
                                 BIT_CANY |
                                 BIT_BREF);
-    
+
 }
diff --git a/src/org/joni/constants/OPCode.java b/src/org/joni/constants/OPCode.java
index 8e06f88..05d1f8b 100644
--- a/src/org/joni/constants/OPCode.java
+++ b/src/org/joni/constants/OPCode.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
@@ -43,7 +43,7 @@ public interface OPCode {
 
     final int CCLASS                        = 16;
     final int CCLASS_MB                     = 17;
-    final int CCLASS_MIX                    = 18;                    
+    final int CCLASS_MIX                    = 18;
     final int CCLASS_NOT                    = 19;
     final int CCLASS_MB_NOT                 = 20;
     final int CCLASS_MIX_NOT                = 21;
@@ -91,14 +91,14 @@ public interface OPCode {
     final int POP                           = 57;
     final int PUSH_OR_JUMP_EXACT1           = 58;           /* if match exact then push, else jump. */
     final int PUSH_IF_PEEK_NEXT             = 59;           /* if match exact then push, else none. */
-    
+
     final int REPEAT                        = 60;           /* {n,m} */
     final int REPEAT_NG                     = 61;           /* {n,m}? (non greedy) */
     final int REPEAT_INC                    = 62;
     final int REPEAT_INC_NG                 = 63;           /* non greedy */
     final int REPEAT_INC_SG                 = 64;           /* search and get in stack */
     final int REPEAT_INC_NG_SG              = 65;           /* search and get in stack (non greedy) */
-    
+
     final int NULL_CHECK_START              = 66;           /* null loop checker start */
     final int NULL_CHECK_END                = 67;           /* null loop checker end   */
     final int NULL_CHECK_END_MEMST          = 68;           /* null loop checker end (with capture status) */
@@ -126,7 +126,7 @@ public interface OPCode {
       /* no need: IS_DYNAMIC_OPTION() == 0 */
     final int SET_OPTION_PUSH               = 86;           /* set option and push recover option */
     final int SET_OPTION                    = 87;           /* set option */
-    
+
     // single byte versions
     final int ANYCHAR_SB                    = 88;           /* "."  */
     final int ANYCHAR_ML_SB                 = 89;           /* "."  multi-line */
@@ -145,12 +145,12 @@ public interface OPCode {
     final int NOT_WORD_BOUND_SB             = 101;
     final int WORD_BEGIN_SB                 = 102;
     final int WORD_END_SB                   = 103;
-    
+
     final int LOOK_BEHIND_SB                = 104;
-    
+
     final int EXACT1_IC_SB                  = 105;           /* single byte, N = 1, ignore case */
     final int EXACTN_IC_SB                  = 106;           /* single byte,        ignore case */
-    
+
 
     public final String OpCodeNames[] = Config.DEBUG_COMPILE ? new String[] {
         "finish", /*OP_FINISH*/
@@ -241,7 +241,7 @@ public interface OPCode {
         "state-check-anychar-ml*", /*OP_STATE_CHECK_ANYCHAR_ML_STAR*/
         "set-option-push", /*OP_SET_OPTION_PUSH*/
         "set-option", /*OP_SET_OPTION*/
-        
+
         // single byte versions
         "anychar-sb", /*OP_ANYCHAR*/
         "anychar-ml-sb", /*OP_ANYCHAR_ML*/
@@ -254,21 +254,21 @@ public interface OPCode {
 
         "cclass-sb", /*OP_CCLASS*/
         "cclass-not-sb", /*OP_CCLASS_NOT*/
-        
+
         "word-sb", /*OP_WORD*/
         "not-word-sb", /*OP_NOT_WORD*/
         "word-bound-sb", /*OP_WORD_BOUND*/
         "not-word-bound-sb", /*OP_NOT_WORD_BOUND*/
         "word-begin-sb", /*OP_WORD_BEGIN*/
         "word-end-sb", /*OP_WORD_END*/
-        
+
         "look-behind-sb", /*OP_LOOK_BEHIND*/
-        
+
         "exact1-ic-sb", /*OP_EXACT1_IC*/
         "exactn-ic-sb", /*OP_EXACTN_IC*/
 
     } : null;
-    
+
     public final int OpCodeArgTypes[] = Config.DEBUG_COMPILE ? new int[] {
         Arguments.NON, /*OP_FINISH*/
         Arguments.NON, /*OP_END*/
@@ -358,7 +358,7 @@ public interface OPCode {
         Arguments.STATE_CHECK, /*OP_STATE_CHECK_ANYCHAR_ML_STAR*/
         Arguments.OPTION, /*OP_SET_OPTION_PUSH*/
         Arguments.OPTION, /*OP_SET_OPTION*/
-        
+
         // single byte versions
         Arguments.NON, /*OP_ANYCHAR*/
         Arguments.NON, /*OP_ANYCHAR_ML*/
@@ -380,7 +380,7 @@ public interface OPCode {
         Arguments.NON, /*OP_WORD_END*/
 
         Arguments.SPECIAL, /*OP_LOOK_BEHIND*/
-        
+
         Arguments.SPECIAL, /*OP_EXACT1_IC*/
         Arguments.SPECIAL, /*OP_EXACTN_IC*/
     } : null;
diff --git a/src/org/joni/constants/OPSize.java b/src/org/joni/constants/OPSize.java
index dcd419b..d5595ad 100644
--- a/src/org/joni/constants/OPSize.java
+++ b/src/org/joni/constants/OPSize.java
@@ -1,26 +1,26 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
 
 public interface OPSize {
-    
+
     // this might be helpful for potential byte[] migration
     final int OPCODE                = 1;
     final int RELADDR               = 1;
@@ -32,9 +32,10 @@ public interface OPSize {
     final int OPTION                = 1;
     final int CODE_POINT            = 1;
     final int POINTER               = 1;
-    
+    final int INDEX                 = 1;
+
     /* op-code + arg size */
-    
+
     final int ANYCHAR_STAR                  = OPCODE;
     final int ANYCHAR_STAR_PEEK_NEXT        = (OPCODE + 1);
     final int JUMP                          = (OPCODE + RELADDR);
diff --git a/src/org/joni/constants/Reduce.java b/src/org/joni/constants/Reduce.java
index e62de7f..07b5e18 100644
--- a/src/org/joni/constants/Reduce.java
+++ b/src/org/joni/constants/Reduce.java
@@ -1,23 +1,24 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
+
 import static org.joni.constants.Reduce.ReduceType.A;
 import static org.joni.constants.Reduce.ReduceType.AQ;
 import static org.joni.constants.Reduce.ReduceType.ASIS;
@@ -27,7 +28,7 @@ import static org.joni.constants.Reduce.ReduceType.P_QQ;
 import static org.joni.constants.Reduce.ReduceType.QQ;
 
 public interface Reduce {
-    
+
     enum ReduceType {
         ASIS,       /* as is */
         DEL,        /* delete parent */
@@ -44,17 +45,17 @@ public interface Reduce {
       {A,       A,      DEL,    ASIS,   P_QQ,   DEL},  /* '+'  */
       {DEL,     AQ,     AQ,     DEL,    AQ,     AQ},   /* '??' */
       {DEL,     DEL,    DEL,    DEL,    DEL,    DEL},  /* '*?' */
-      {ASIS,    PQ_Q,   DEL,    AQ,     AQ,     DEL}   /* '+?' */     
+      {ASIS,    PQ_Q,   DEL,    AQ,     AQ,     DEL}   /* '+?' */
     };
-    
-    
+
+
     final String PopularQStr[] = new String[] {
         "?", "*", "+", "??", "*?", "+?"
     };
-    
+
     String ReduceQStr[]= new String[] {
         "", "", "*", "*?", "??", "+ and ??", "+? and ?"
-    };    
-    
+    };
+
 }
 
diff --git a/src/org/joni/constants/RegexState.java b/src/org/joni/constants/RegexState.java
index acc6d84..72dd3ff 100644
--- a/src/org/joni/constants/RegexState.java
+++ b/src/org/joni/constants/RegexState.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/StackPopLevel.java b/src/org/joni/constants/StackPopLevel.java
index f1f93bd..f5be8f4 100644
--- a/src/org/joni/constants/StackPopLevel.java
+++ b/src/org/joni/constants/StackPopLevel.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/StackType.java b/src/org/joni/constants/StackType.java
index 34ea41f..4a6527c 100644
--- a/src/org/joni/constants/StackType.java
+++ b/src/org/joni/constants/StackType.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/StringType.java b/src/org/joni/constants/StringType.java
index 46972e4..e1a6239 100644
--- a/src/org/joni/constants/StringType.java
+++ b/src/org/joni/constants/StringType.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/SyntaxProperties.java b/src/org/joni/constants/SyntaxProperties.java
index 3fd5b4c..61f2269 100644
--- a/src/org/joni/constants/SyntaxProperties.java
+++ b/src/org/joni/constants/SyntaxProperties.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
@@ -96,14 +96,14 @@ public interface SyntaxProperties {
     /* syntax (behavior); warning */
     final int WARN_CC_OP_NOT_ESCAPED          = (1<<24); /* [,-,] */
     final int WARN_REDUNDANT_NESTED_REPEAT    = (1<<25); /* (?:a*);+ */
-    
+
     final int POSIX_COMMON_OP =
                             OP_DOT_ANYCHAR | OP_POSIX_BRACKET |
                             OP_DECIMAL_BACKREF |
                             OP_BRACKET_CC | OP_ASTERISK_ZERO_INF |
                             OP_LINE_ANCHOR |
                             OP_ESC_CONTROL_CHARS;
-    
+
     final int GNU_REGEX_OP =
                             OP_DOT_ANYCHAR | OP_BRACKET_CC |
                             OP_POSIX_BRACKET | OP_DECIMAL_BACKREF |
@@ -116,7 +116,7 @@ public interface SyntaxProperties {
                             OP_ESC_B_WORD_BOUND | OP_ESC_LTGT_WORD_BEGIN_END |
                             OP_ESC_S_WHITE_SPACE | OP_ESC_D_DIGIT |
                             OP_LINE_ANCHOR;
-    
+
     final int GNU_REGEX_BV =
                             CONTEXT_INDEP_ANCHORS | CONTEXT_INDEP_REPEAT_OPS |
                             CONTEXT_INVALID_REPEAT_OPS | ALLOW_INVALID_INTERVAL |
diff --git a/src/org/joni/constants/TargetInfo.java b/src/org/joni/constants/TargetInfo.java
index 3fdbe5a..ffec783 100644
--- a/src/org/joni/constants/TargetInfo.java
+++ b/src/org/joni/constants/TargetInfo.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/TokenType.java b/src/org/joni/constants/TokenType.java
index 9ea159d..59aa094 100644
--- a/src/org/joni/constants/TokenType.java
+++ b/src/org/joni/constants/TokenType.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/constants/Traverse.java b/src/org/joni/constants/Traverse.java
index 1c08ea5..035e9f7 100644
--- a/src/org/joni/constants/Traverse.java
+++ b/src/org/joni/constants/Traverse.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.constants;
diff --git a/src/org/joni/exception/ErrorMessages.java b/src/org/joni/exception/ErrorMessages.java
index d1c1279..f490713 100644
--- a/src/org/joni/exception/ErrorMessages.java
+++ b/src/org/joni/exception/ErrorMessages.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.exception;
@@ -24,10 +24,10 @@ import org.joni.Config;
 public interface ErrorMessages extends org.jcodings.exception.ErrorMessages {
     final String MISMATCH = "mismatch";
     final String NO_SUPPORT_CONFIG = "no support in this configuration";
-    
+
     /* internal error */
     final String ERR_MEMORY = "fail to memory allocation";
-    final String ERR_MATCH_STACK_LIMIT_OVER = "match-stack limit over"; 
+    final String ERR_MATCH_STACK_LIMIT_OVER = "match-stack limit over";
     final String ERR_TYPE_BUG = "undefined type (bug)";
     final String ERR_PARSER_BUG = "internal parser error (bug)";
     final String ERR_STACK_BUG = "stack error (bug)";
@@ -38,7 +38,7 @@ public interface ErrorMessages extends org.jcodings.exception.ErrorMessages {
 
     /* general error */
     final String ERR_INVALID_ARGUMENT = "invalid argument";
-    
+
     /* syntax error */
     final String ERR_END_PATTERN_AT_LEFT_BRACE = "end pattern at left brace";
     final String ERR_END_PATTERN_AT_LEFT_BRACKET = "end pattern at left bracket";
@@ -62,7 +62,7 @@ public interface ErrorMessages extends org.jcodings.exception.ErrorMessages {
     final String ERR_INVALID_POSIX_BRACKET_TYPE = "invalid POSIX bracket type";
     final String ERR_INVALID_LOOK_BEHIND_PATTERN = "invalid pattern in look-behind";
     final String ERR_INVALID_REPEAT_RANGE_PATTERN = "invalid repeat range {lower,upper}";
-    
+
     /* values error (syntax error) */
     final String ERR_TOO_BIG_NUMBER = "too big number";
     final String ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE = "too big number for repeat range";
diff --git a/src/org/joni/exception/InternalException.java b/src/org/joni/exception/InternalException.java
index 959f44f..b41e0aa 100644
--- a/src/org/joni/exception/InternalException.java
+++ b/src/org/joni/exception/InternalException.java
@@ -1,27 +1,27 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.exception;
 
 public class InternalException extends JOniException{
     private static final long serialVersionUID = -3871816465397927992L;
-    
+
     public InternalException(String message) {
         super(message);
     }
diff --git a/src/org/joni/exception/JOniException.java b/src/org/joni/exception/JOniException.java
index f5d728c..c2c5d47 100644
--- a/src/org/joni/exception/JOniException.java
+++ b/src/org/joni/exception/JOniException.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.exception;
diff --git a/src/org/joni/exception/SyntaxException.java b/src/org/joni/exception/SyntaxException.java
index 7b00d1c..7e50cf5 100644
--- a/src/org/joni/exception/SyntaxException.java
+++ b/src/org/joni/exception/SyntaxException.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.exception;
diff --git a/src/org/joni/exception/ValueException.java b/src/org/joni/exception/ValueException.java
index b07ac42..790f354 100644
--- a/src/org/joni/exception/ValueException.java
+++ b/src/org/joni/exception/ValueException.java
@@ -1,35 +1,35 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.exception;
 
 public class ValueException extends SyntaxException{
     private static final long serialVersionUID = -196013852479929134L;
-    
+
     public ValueException(String message) {
         super(message);
     }
-    
+
     public ValueException(String message, String str) {
         super(message.replaceAll("%n", str));
     }
-    
+
     public ValueException(String message, byte[]bytes, int p, int end) {
         this(message, new String(bytes, p, end - p));
     }
diff --git a/test/org/joni/test/Test.java b/test/org/joni/test/Test.java
index 1a9cec6..dda5c3e 100644
--- a/test/org/joni/test/Test.java
+++ b/test/org/joni/test/Test.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -31,31 +31,32 @@ import org.jcodings.Encoding;
 import org.joni.exception.JOniException;
 
 public abstract class Test {
+    static final boolean VERBOSE = false;
 
     int nsucc;
     int nerror;
     int nfail;
-    
+
     public abstract int option();
     public abstract Encoding encoding();
     public abstract String testEncoding();
     public abstract Syntax syntax();
-    
+
     protected String repr(byte[]bytes) {
         return new String(bytes);
     }
-    
+
     protected int length(byte[]bytes) {
         return bytes.length;
     }
-    
+
     public void xx(byte[]pattern, byte[]str, int from, int to, int mem, boolean not) {
         xx(pattern, str, from, to, mem, not, option());
     }
-    
+
     public void xx(byte[]pattern, byte[]str, int from, int to, int mem, boolean not, int option) {
         Regex reg;
-        
+
         try {
             reg = new Regex(pattern, 0, length(pattern), option, encoding(), syntax());
         } catch (JOniException je) {
@@ -69,9 +70,9 @@ public abstract class Test {
             e.printStackTrace(Config.err);
             Config.err.println("SEVERE ERROR: " + e.getMessage());
             nerror++;
-            return;            
+            return;
         }
-        
+
         Matcher m = reg.matcher(str, 0, length(str));
         Region region;
 
@@ -90,12 +91,12 @@ public abstract class Test {
             e.printStackTrace(Config.err);
             Config.err.println("SEVERE ERROR: " + e.getMessage());
             nerror++;
-            return;            
+            return;
         }
-        
+
         if (r == -1) {
             if (not) {
-                Config.log.println("OK(N): /" + repr(pattern) + "/ '" + repr(str) + "'");
+                if (VERBOSE) Config.log.println("OK(N): /" + repr(pattern) + "/ '" + repr(str) + "'");
                 nsucc++;
             } else {
                 Config.log.println("FAIL: /" + repr(pattern) + "/ '" + repr(str) + "'");
@@ -107,7 +108,7 @@ public abstract class Test {
                 nfail++;
             } else {
                 if (region.beg[mem] == from && region.end[mem] == to) {
-                    Config.log.println("OK: /" + repr(pattern) + "/ '" +repr(str) + "'");
+                    if (VERBOSE)  Config.log.println("OK: /" + repr(pattern) + "/ '" +repr(str) + "'");
                     nsucc++;
                 } else {
                     Config.log.println("FAIL: /" + repr(pattern) + "/ '" + repr(str) + "' " +
@@ -118,23 +119,31 @@ public abstract class Test {
             }
         }
     }
-    
+
     protected void x2(byte[]pattern, byte[]str, int from, int to) {
         xx(pattern, str, from, to, 0, false);
     }
-    
+
+    protected void x2(byte[]pattern, byte[]str, int from, int to, int option) {
+        xx(pattern, str, from, to, 0, false, option);
+    }
+
     protected void x3(byte[]pattern, byte[]str, int from, int to, int mem) {
         xx(pattern, str, from, to, mem, false);
     }
-    
+
     protected void n(byte[]pattern, byte[]str) {
         xx(pattern, str, 0, 0, 0, true);
     }
 
+    protected void n(byte[]pattern, byte[]str, int option) {
+        xx(pattern, str, 0, 0, 0, true, option);
+    }
+
     public void xxs(String pattern, String str, int from, int to, int mem, boolean not) {
         xxs(pattern, str, from, to, mem, not, option());
     }
-    
+
     public void xxs(String pattern, String str, int from, int to, int mem, boolean not, int option) {
         try{
             xx(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, mem, not, option);
@@ -146,7 +155,7 @@ public abstract class Test {
     public void x2s(String pattern, String str, int from, int to) {
         x2s(pattern, str, from, to, option());
     }
-    
+
     public void x2s(String pattern, String str, int from, int to, int option) {
         try{
         xx(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, 0, false, option);
@@ -154,41 +163,40 @@ public abstract class Test {
             uee.printStackTrace();
         }
     }
-    
+
     public void x3s(String pattern, String str, int from, int to, int mem) {
         x3s(pattern, str, from, to, mem, option());
     }
-    
+
     public void x3s(String pattern, String str, int from, int to, int mem, int option) {
-        try{        
+        try{
             xx(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, mem, false, option);
         } catch (UnsupportedEncodingException uee) {
             uee.printStackTrace();
         }
     }
-    
+
     public void ns(String pattern, String str) {
         ns(pattern, str, option());
     }
-    
+
     public void ns(String pattern, String str, int option) {
-        try{        
+        try{
             xx(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), 0, 0, 0, true, option);
         } catch (UnsupportedEncodingException uee) {
             uee.printStackTrace();
         }
     }
-    
+
     public void printResults() {
-        Config.log.println("\nRESULT   SUCC: " + nsucc + ",  FAIL: " + nfail + ",  ERROR: " + nerror +
-                "   (by JONI)");
+        Config.log.println("RESULT   SUCC: " + nsucc + ",  FAIL: " + nfail + ",  ERROR: " + nerror + " Test: " + getClass().getSimpleName() + ", Encoding: " + encoding());
     }
-    
+
     public abstract void test();
-    
+
     public final void run() {
         test();
         printResults();
     }
-    
+
 }
diff --git a/test/org/joni/test/TestA.java b/test/org/joni/test/TestA.java
index bc3ebf5..f73153a 100644
--- a/test/org/joni/test/TestA.java
+++ b/test/org/joni/test/TestA.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -33,14 +33,14 @@ public class TestA extends Test {
     public Encoding encoding() {
         return ASCIIEncoding.INSTANCE;
     }
-    
+
     public String testEncoding() {
         return "iso-8859-2";
     }
 
     public Syntax syntax() {
         return Syntax.DEFAULT;
-    }   
+    }
 
     public void test() {
         x2s("", "", 0, 0);
@@ -451,11 +451,11 @@ public class TestA extends Test {
         x2s("x((.)*)*x(?i:\\1)\\Z", "0x1x2x1X2", 1, 9);
         x2s("(?:()|()|()|()|()|())*\\2\\5", "", 0, 0);
         x2s("(?:()|()|()|(x)|()|())*\\2b\\5", "b", 0, 1);
-        
+
         x3s("\\A(?<a>|.|(?:(?<b>.)\\g<a>\\k<b+0>))\\z", "reer", 0, 4, 1);
         x3s("(?-i:\\g<name>)(?i:(?<name>a)){0}", "A", 0, 1, 1);
-        
-        String pat = 
+
+        String pat =
             "(?<element> \\g<stag> \\g<content>* \\g<etag> ){0}" +
             "(?<stag> < \\g<name> \\s* > ){0}" +
             "(?<name> [a-zA-Z_:]+ ){0}" +
@@ -464,7 +464,7 @@ public class TestA extends Test {
             "\\g<element>";
 
         String str = "<foo>f<bar>bbb</bar>f</foo>";
-        
+
         x3s(pat, str, 0, 27, 0, Option.EXTEND);
         x3s(pat, str, 0, 27, 1, Option.EXTEND);
         x3s(pat, str, 6, 11, 2, Option.EXTEND);
@@ -473,8 +473,32 @@ public class TestA extends Test {
         x3s(pat, str, 21, 27, 5, Option.EXTEND);
 
         x2s("(a)b\\k<1>", "aba", 0, 3);
+        x2s("^(?>(?=a)(a|))++$", "a", 0, 1);
+        x2s("\\k", "k", 0, 1);
+        x2s("\\kx", "kx", 0, 2);
+        x2s("\\g", "g", 0, 1);
+        x2s("\\gx", "gx", 0, 2);
+        x2s("\\k\\g", "kg", 0, 2);
+        ns("\\00", "00");
+        ns("\\70", "70");
+        x2s("\\80", "80", 0, 2);
+        x2s("\\90", "90", 0, 2);
+
+        ns("(?<!b|aa)c", "Aac", Option.IGNORECASE);
+        x2s("(?<!b|aa)", "Aac", 0, 0, Option.IGNORECASE);
+        x2s("(?<=b|aa)c", "Aac", 2, 3, Option.IGNORECASE);
+        x2s("(?<=b|aa)", "Aac", 2, 2, Option.IGNORECASE);
+
+        ns("\\A[a-f&&[^b-c]&&[^e]]\\z", "e");
+        ns("[[^a]&&e&&[^e]]", "e");
+
+        x2s("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 35, Option.IGNORECASE);
+        x2s("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 0, 35, Option.IGNORECASE);
+        x2s("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAA", 0, 35, Option.IGNORECASE);
+
+        x2s("(?mix)", "", 0, 0);
     }
-    
+
     public static void main(String[] args) throws Throwable{
         new TestA().run();
     }
diff --git a/test/org/joni/test/TestC.java b/test/org/joni/test/TestC.java
index 94f4218..9af3941 100644
--- a/test/org/joni/test/TestC.java
+++ b/test/org/joni/test/TestC.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -34,14 +34,14 @@ public class TestC extends Test {
     public Encoding encoding() {
         return EUCJPEncoding.INSTANCE;
     }
-    
+
     public String testEncoding() {
         return "cp1250";
     }
 
     public Syntax syntax() {
         return Syntax.DEFAULT;
-    }   
+    }
 
     public void test() {
         x2s("", "", 0, 0);
@@ -465,9 +465,9 @@ public class TestC extends Test {
         if (Config.VANILLA) x2s("\\xca\\xb8", "\u0118\u00b8", 0, 2);
         x2s(".", "\u00a4\u02d8", 0, 2);
         x2s("..", "\u00a4\u00ab\u00a4\u00ad", 0, 4);
-        x2s("\\w", "\u00a4\u015e", 0, 2);
-        ns("\\W", "\u00a4\u02d8");
-        x2s("[\\W]", "\u00a4\u00a6$", 2, 3);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\\w", "\u00a4\u015e", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\\W", "\u00a4\u02d8");
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("[\\W]", "\u00a4\u00a6$", 2, 3);
         x2s("\\S", "\u00a4\u02dd", 0, 2);
         x2s("\\S", "\u00b4\u00c1", 0, 2);
         x2s("\\b", "\u00b5\u00a4 ", 0, 0);
@@ -479,27 +479,27 @@ public class TestC extends Test {
         ns("[\u00a4\u0118\u00a4\u00cb]", "\u00a4\u011a");
         x2s("[\u00a4\u00a6-\u00a4\u015e]", "\u00a4\u00a8", 0, 2);
         ns("[^\u00a4\u00b1]", "\u00a4\u00b1");
-        x2s("[\\w]", "\u00a4\u00cd", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("[\\w]", "\u00a4\u00cd", 0, 2);
         ns("[\\d]", "\u00a4\u0150");
         x2s("[\\D]", "\u00a4\u010e", 0, 2);
         ns("[\\s]", "\u00a4\u017b");
         x2s("[\\S]", "\u00a4\u0158", 0, 2);
-        x2s("[\\w\\d]", "\u00a4\u010d", 0, 2);
-        x2s("[\\w\\d]", "   \u00a4\u010d", 3, 5);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("[\\w\\d]", "\u00a4\u010d", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("[\\w\\d]", "   \u00a4\u010d", 3, 5);
         ns("\\w\u00b5\u00b4\u013d\u00d6", " \u00b5\u00b4\u013d\u00d6");
         x2s("\u00b5\u00b4\\W\u013d\u00d6", "\u00b5\u00b4 \u013d\u00d6", 0, 5);
         x2s("\u00a4\u02d8.\u00a4\u00a4.\u00a4\u00a6", "\u00a4\u02d8\u00a4\u02d8\u00a4\u00a4\u00a4\u00a4\u00a4\u00a6", 0, 10);
-        x2s(".\\w\u00a4\u00a6\\W..\u00a4\u013e", "\u00a4\u00a8\u00a4\u00a6\u00a4\u00a6 \u00a4\u00a6\u00a4\u013e\u00a4\u013e", 0, 13);
-        x2s("\\s\\w\u00a4\u0142\u00a4\u0142\u00a4\u0142", " \u00a4\u0142\u00a4\u0142\u00a4\u0142\u00a4\u0142", 0, 9);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s(".\\w\u00a4\u00a6\\W..\u00a4\u013e", "\u00a4\u00a8\u00a4\u00a6\u00a4\u00a6 \u00a4\u00a6\u00a4\u013e\u00a4\u013e", 0, 13);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\\s\\w\u00a4\u0142\u00a4\u0142\u00a4\u0142", " \u00a4\u0142\u00a4\u0142\u00a4\u0142\u00a4\u0142", 0, 9);
         x2s("\u00a4\u02d8\u00a4\u02d8.\u00a4\u00b1", "\u00a4\u02d8\u00a4\u02d8\u00a4\u00b1\u00a4\u00b1", 0, 8);
         ns(".\u00a4\u00a4", "\u00a4\u00a4\u00a4\u00a8");
         x2s(".\u00a4\u015e", "\u00a4\u015e\u00a4\u015e", 0, 4);
         x2s("^\u00a4\u02d8", "\u00a4\u02d8", 0, 2);
         x2s("^\u00a4\u0155$", "\u00a4\u0155", 0, 2);
-        x2s("^\\w$", "\u00a4\u00cb", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("^\\w$", "\u00a4\u00cb", 0, 2);
         x2s("^\\w\u00a4\u00ab\u00a4\u00ad\u00a4\u017b\u00a4\u00b1\u00a4\u0142$", "z\u00a4\u00ab\u00a4\u00ad\u00a4\u017b\u00a4\u00b1\u00a4\u0142", 0, 11);
         x2s("^\\w...\u00a4\u00a6\u00a4\u00a8\u00a4\u015e$", "z\u00a4\u02d8\u00a4\u00a4\u00a4\u00a6\u00a4\u00a6\u00a4\u00a8\u00a4\u015e", 0, 13);
-        x2s("\\w\\w\\s\\W\u00a4\u015e\u00a4\u015e\u00a4\u015e\\d", "a\u00a4\u015e  \u00a4\u015e\u00a4\u015e\u00a4\u015e4", 0, 12);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\\w\\w\\s\\W\u00a4\u015e\u00a4\u015e\u00a4\u015e\\d", "a\u00a4\u015e  \u00a4\u015e\u00a4\u015e\u00a4\u015e4", 0, 12);
         x2s("\\A\u00a4\u017c\u00a4\u00c1\u00a4\u00c4", "\u00a4\u017c\u00a4\u00c1\u00a4\u00c4", 0, 6);
         x2s("\u00a4\u0155\u00a4\u00e1\u00a4\u00e2\\Z", "\u00a4\u0155\u00a4\u00e1\u00a4\u00e2", 0, 6);
         x2s("\u00a4\u00ab\u00a4\u00ad\u00a4\u017b\\z", "\u00a4\u00ab\u00a4\u00ad\u00a4\u017b", 0, 6);
@@ -555,9 +555,9 @@ public class TestC extends Test {
         x2s("\u00b5\u00b4|\u013d\u00d6\\Z", "\u013d\u00d6\n", 0, 2);
         x2s("\u00b5\u00b4|\u013d\u00d6\\z", "\u013d\u00d6\u00b5\u00b4", 2, 4);
         x2s("\u00b5\u00b4|\u013d\u00d6\\z", "\u013d\u00d6", 0, 2);
-        x2s("\\w|\\s", "\u00a4\u015e", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\\w|\\s", "\u00a4\u015e", 0, 2);
         x2s("\\w|%", "%\u00a4\u015e", 0, 1);
-        x2s("\\w|[&$]", "\u00a4\u00a6&", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\\w|[&$]", "\u00a4\u00a6&", 0, 2);
         x2s("[\u00a4\u00a4-\u00a4\u00b1]", "\u00a4\u00a6", 0, 2);
         x2s("[\u00a4\u00a4-\u00a4\u00b1]|[^\u00a4\u00ab-\u00a4\u0142]", "\u00a4\u02d8", 0, 2);
         x2s("[\u00a4\u00a4-\u00a4\u00b1]|[^\u00a4\u00ab-\u00a4\u0142]", "\u00a4\u00ab", 0, 2);
@@ -688,7 +688,7 @@ public class TestC extends Test {
         x3s("(((((((\u00a4\u00dd*)\u00a4\u00da))))))\u00a4\u00d4\\7", "\u00a4\u00dd\u00a4\u00dd\u00a4\u00dd\u00a4\u00da\u00a4\u00d4\u00a4\u00dd\u00a4\u00dd\u00a4\u00dd", 0, 6, 7);
         x2s("(\u00a4\u010e)(\u00a4\u0147)(\u00a4\u0150)\\2\\1\\3", "\u00a4\u010e\u00a4\u0147\u00a4\u0150\u00a4\u0147\u00a4\u010e\u00a4\u0150", 0, 12);
         x2s("([\u00a4\u00ad-\u00a4\u00b1])\\1", "\u00a4\u017b\u00a4\u017b", 0, 4);
-        x2s("(\\w\\d\\s)\\1", "\u00a4\u02d85 \u00a4\u02d85 ", 0, 8);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("(\\w\\d\\s)\\1", "\u00a4\u02d85 \u00a4\u02d85 ", 0, 8);
         ns("(\\w\\d\\s)\\1", "\u00a4\u02d85 \u00a4\u02d85");
         x2s("(\u0102\u017b\u02c7\u00a9|[\u00a4\u02d8-\u00a4\u00a6]{3})\\1", "\u0102\u017b\u02c7\u00a9\u0102\u017b\u02c7\u00a9", 0, 8);
         x2s("...(\u0102\u017b\u02c7\u00a9|[\u00a4\u02d8-\u00a4\u00a6]{3})\\1", "\u00a4\u02d8a\u00a4\u02d8\u0102\u017b\u02c7\u00a9\u0102\u017b\u02c7\u00a9", 0, 13);
@@ -729,7 +729,7 @@ public class TestC extends Test {
         x2s("a<b>\u0104\u0110\u02c7\u013d\u0104\u00b8\u0104\u00e7\u0104\u00f3\u00a4\u00ce\u0104\u0154\u0104\u00a6\u0104\u00f3\u0104\u00ed\u02c7\u013d\u0104\u00c9<\\/b>", "a<b>\u0104\u0110\u02c7\u013d\u0104\u00b8\u0104\u00e7\u0104\u00f3\u00a4\u00ce\u0104\u0154\u0104\u00a6\u0104\u00f3\u0104\u00ed\u02c7\u013d\u0104\u00c9</b>", 0, 32);
         x2s(".<b>\u0104\u0110\u02c7\u013d\u0104\u00b8\u0104\u00e7\u0104\u00f3\u00a4\u00ce\u0104\u0154\u0104\u00a6\u0104\u00f3\u0104\u00ed\u02c7\u013d\u0104\u00c9<\\/b>", "a<b>\u0104\u0110\u02c7\u013d\u0104\u00b8\u0104\u00e7\u0104\u00f3\u00a4\u00ce\u0104\u0154\u0104\u00a6\u0104\u00f3\u0104\u00ed\u02c7\u013d\u0104\u00c9</b>", 0, 32);
     }
-    
+
     public static void main(String[] args) throws Throwable{
         new TestC().run();
     }
diff --git a/test/org/joni/test/TestCornerCases.java b/test/org/joni/test/TestCornerCases.java
index b2b8377..717766d 100644
--- a/test/org/joni/test/TestCornerCases.java
+++ b/test/org/joni/test/TestCornerCases.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -35,14 +35,14 @@ public class TestCornerCases extends Test {
     public Encoding encoding() {
         return ASCIIEncoding.INSTANCE;
     }
-    
+
     public String testEncoding() {
         return "cp1250";
     }
 
     public Syntax syntax() {
         return Syntax.DEFAULT;
-    }   
+    }
 
     public void test() {
         byte[] reg = "l.".getBytes();
@@ -55,7 +55,7 @@ public class TestCornerCases extends Test {
             nfail++;
         }
     }
-    
+
     public static void main(String[] args) throws Throwable{
         new TestCornerCases().run();
     }
diff --git a/test/org/joni/test/TestCrnl.java b/test/org/joni/test/TestCrnl.java
index a8ef6a6..9a8c65c 100644
--- a/test/org/joni/test/TestCrnl.java
+++ b/test/org/joni/test/TestCrnl.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -34,14 +34,14 @@ public class TestCrnl extends Test {
     public Encoding encoding() {
         return ASCIIEncoding.INSTANCE;
     }
-    
+
     public String testEncoding() {
         return "ascii";
     }
 
     public Syntax syntax() {
         return Syntax.DEFAULT;
-    }   
+    }
 
     public void test() {
         x2s("",        "\r\n",        0,  0);
@@ -76,10 +76,10 @@ public class TestCrnl extends Test {
         x2s("(?=a\\Z).",     "a\r\n",       0,  1);
         ns("(?=a\\Z).",     "a\r");
         x2s("(?!a\\Z)..",    "a\r",         0,  2);
-        
+
         if (nfail > 0 || nerror > 0) Config.err.println("make sure to enable USE_CRNL_AS_LINE_TERMINATOR");
     }
-    
+
     public static void main(String[] args) throws Throwable{
         new TestCrnl().run();
     }
diff --git a/test/org/joni/test/TestJoni.java b/test/org/joni/test/TestJoni.java
index f1cb9ec..a94ed56 100644
--- a/test/org/joni/test/TestJoni.java
+++ b/test/org/joni/test/TestJoni.java
@@ -1,34 +1,57 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
 package org.joni.test;
 
 import junit.framework.TestCase;
 
 public class TestJoni extends TestCase {
-    
+
     private Test testa;
     private Test testc;
     private Test testu;
     private Test testnsu8;
-    
+    private Test testLookBehind;
+    private Test testu8;
+
     protected void setUp() {
         testa = new TestA();
         testc = new TestC();
         testu = new TestU();
         testnsu8 = new TestNSU8();
+        testu8 = new TestU8();
+        testLookBehind = new TestLookBehind();
     }
-    
+
     protected void tearDown() {
     }
-    
+
     private void testJoniTest(Test test) {
         test.run();
         assertEquals(test.nerror, 0);
         assertEquals(test.nfail, 0);
     }
-    
+
     public void testAscii() {
         testJoniTest(testa);
     }
-    
+
     public void testEUCJP() {
         testJoniTest(testc);
     }
@@ -36,5 +59,10 @@ public class TestJoni extends TestCase {
     public void testUnicode() {
         testJoniTest(testu);
         testJoniTest(testnsu8);
+        testJoniTest(testu8);
+    }
+
+    public void testLookBehind() {
+    	testJoniTest(testLookBehind);
     }
 }
diff --git a/src/org/joni/OptEnvironment.java b/test/org/joni/test/TestLookBehind.java
similarity index 57%
copy from src/org/joni/OptEnvironment.java
copy to test/org/joni/test/TestLookBehind.java
index 4b59c31..9ae4379 100644
--- a/src/org/joni/OptEnvironment.java
+++ b/test/org/joni/test/TestLookBehind.java
@@ -1,39 +1,54 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-package org.joni;
+package org.joni.test;
 
 import org.jcodings.Encoding;
+import org.jcodings.specific.ASCIIEncoding;
+import org.joni.Option;
+import org.joni.Syntax;
+
+public class TestLookBehind extends Test {
+
+	public int option() {
+		return Option.DEFAULT;
+	}
+
+	public Encoding encoding() {
+		return ASCIIEncoding.INSTANCE;
+	}
+
+	public String testEncoding() {
+		return "iso-8859-1";
+	}
+
+	public Syntax syntax() {
+		return Syntax.DEFAULT;
+	}
+
+	@Override
+	public void test() {
+		x2s("(?<=a).*b", "aab", 1, 3);
+	}
+
+	public static void main(String[] args) throws Throwable {
+		new TestLookBehind().run();
+	}
 
-// remove this one in future and pass mmd directly
-final class OptEnvironment {
-    final MinMaxLen mmd = new MinMaxLen();
-    Encoding enc;
-    int options;
-    int caseFoldFlag;
-    ScanEnvironment scanEnv;
-    
-    void copy(OptEnvironment other) {
-        mmd.copy(other.mmd);
-        enc = other.enc;
-        options = other.options;
-        caseFoldFlag = other.caseFoldFlag;
-        scanEnv = other.scanEnv;
-    }
 }
diff --git a/test/org/joni/test/TestNSU8.java b/test/org/joni/test/TestNSU8.java
index 54dd902..ec4ed2d 100644
--- a/test/org/joni/test/TestNSU8.java
+++ b/test/org/joni/test/TestNSU8.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -29,19 +29,19 @@ public class TestNSU8 extends Test {
     public int option() {
         return Option.DEFAULT;
     }
-    
+
     public Encoding encoding() {
         return NonStrictUTF8Encoding.INSTANCE;
     }
-    
+
     public String testEncoding() {
         return "utf-8";
     }
-    
+
     public Syntax syntax() {
         return Syntax.DEFAULT;
     }
-    
+
     public void test() {
         xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)240, (byte)32, (byte)32, (byte)32, (byte)32}, 0, 5, 1, false);
         xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)240, (byte)32, (byte)32, (byte)32}, 0, 4, 1, false);
@@ -58,7 +58,7 @@ public class TestNSU8 extends Test {
         xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)192, (byte)32}, 0, 2, 1, false);
         xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)192}, 0, 1, 1, false);
     }
-    
+
     public static void main(String[] args) throws Throwable {
         new TestNSU8().run();
     }
diff --git a/test/org/joni/test/TestU.java b/test/org/joni/test/TestU.java
index ee12227..eb4a2a7 100644
--- a/test/org/joni/test/TestU.java
+++ b/test/org/joni/test/TestU.java
@@ -1,20 +1,20 @@
 /*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of 
- * this software and associated documentation files (the "Software"), to deal in 
- * the Software without restriction, including without limitation the rights to 
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  * of the Software, and to permit persons to whom the Software is furnished to do
  * so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
 package org.joni.test;
@@ -29,23 +29,23 @@ public class TestU extends Test {
     public int option() {
         return Option.DEFAULT;
     }
-    
+
     public Encoding encoding() {
         return UTF16BEEncoding.INSTANCE;
     }
-    
+
     public String testEncoding() {
         return "iso-8859-1";
     }
-    
+
     public Syntax syntax() {
         return Syntax.DEFAULT;
     }
-    
+
     private int ulen(byte[]bytes) {
         return encoding().strByteLengthNull(bytes, 0, bytes.length);
     }
-    
+
     private String uconv(byte []bytes, int len) {
         StringBuilder sb = new StringBuilder();
 
@@ -61,22 +61,22 @@ public class TestU extends Test {
                 }
             } else {
                 sb.append(String.format("\\%03o", c));
-                c = bytes[i+1] & 0xff;                
+                c = bytes[i+1] & 0xff;
                 sb.append(String.format("\\%03o", c));
             }
         }
-        
+
         return sb.toString();
     }
-    
+
     protected String repr(byte[]bytes) {
         return uconv(bytes, ulen(bytes));
     }
-    
+
     protected int length(byte[]bytes) {
         return ulen(bytes);
     }
-    
+
     public void test() {
         x2s("\000\000", "\000\000", 0, 0);
         x2s("\000^\000\000", "\000\000", 0, 0);
@@ -107,12 +107,12 @@ public class TestU extends Test {
         x2s("\000.\000\000", "\000a\000\000", 0, 2);
         ns("\000.\000\000", "\000\000");
         x2s("\000.\000.\000\000", "\000a\000b\000\000", 0, 4);
-        x2s("\000\134\000w\000\000", "\000e\000\000", 0, 2);
-        ns("\000\134\000W\000\000", "\000e\000\000");
-        x2s("\000\134\000s\000\000", "\000 \000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000w\000\000", "\000e\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\000\134\000W\000\000", "\000e\000\000");
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000s\000\000", "\000 \000\000", 0, 2);
         x2s("\000\134\000S\000\000", "\000b\000\000", 0, 2);
-        x2s("\000\134\000d\000\000", "\0004\000\000", 0, 2);
-        ns("\000\134\000D\000\000", "\0004\000\000");
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000d\000\000", "\0004\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\000\134\000D\000\000", "\0004\000\000");
         x2s("\000\134\000b\000\000", "\000z\000 \000\000", 0, 0);
         x2s("\000\134\000b\000\000", "\000 \000z\000\000", 2, 2);
         x2s("\000\134\000B\000\000", "\000z\000z\000 \000\000", 2, 2);
@@ -128,18 +128,18 @@ public class TestU extends Test {
         x2s("\000[\000\134\000^\000]\000+\000\000", "\0000\000^\000^\0001\000\000", 2, 6);
         x2s("\000[\000b\000-\000]\000\000", "\000b\000\000", 0, 2);
         x2s("\000[\000b\000-\000]\000\000", "\000-\000\000", 0, 2);
-        x2s("\000[\000\134\000w\000]\000\000", "\000z\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000[\000\134\000w\000]\000\000", "\000z\000\000", 0, 2);
         ns("\000[\000\134\000w\000]\000\000", "\000 \000\000");
-        x2s("\000[\000\134\000W\000]\000\000", "\000b\000$\000\000", 2, 4);
-        x2s("\000[\000\134\000d\000]\000\000", "\0005\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000[\000\134\000W\000]\000\000", "\000b\000$\000\000", 2, 4);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000[\000\134\000d\000]\000\000", "\0005\000\000", 0, 2);
         ns("\000[\000\134\000d\000]\000\000", "\000e\000\000");
         x2s("\000[\000\134\000D\000]\000\000", "\000t\000\000", 0, 2);
-        ns("\000[\000\134\000D\000]\000\000", "\0003\000\000");
-        x2s("\000[\000\134\000s\000]\000\000", "\000 \000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\000[\000\134\000D\000]\000\000", "\0003\000\000");
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000[\000\134\000s\000]\000\000", "\000 \000\000", 0, 2);
         ns("\000[\000\134\000s\000]\000\000", "\000a\000\000");
         x2s("\000[\000\134\000S\000]\000\000", "\000b\000\000", 0, 2);
-        ns("\000[\000\134\000S\000]\000\000", "\000 \000\000");
-        x2s("\000[\000\134\000w\000\134\000d\000]\000\000", "\0002\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\000[\000\134\000S\000]\000\000", "\000 \000\000");
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000[\000\134\000w\000\134\000d\000]\000\000", "\0002\000\000", 0, 2);
         ns("\000[\000\134\000w\000\134\000d\000]\000\000", "\000 \000\000");
         x2s("\000[\000[\000:\000u\000p\000p\000e\000r\000:\000]\000]\000\000", "\000B\000\000", 0, 2);
         x2s("\000[\000*\000[\000:\000x\000d\000i\000g\000i\000t\000:\000]\000+\000]\000\000", "\000+\000\000", 0, 2);
@@ -176,19 +176,19 @@ public class TestU extends Test {
         ns("\000\134\000w\000a\000b\000c\000\000", "\000 \000a\000b\000c\000\000");
         x2s("\000a\000\134\000W\000b\000c\000\000", "\000a\000 \000b\000c\000\000", 0, 8);
         x2s("\000a\000.\000b\000.\000c\000\000", "\000a\000a\000b\000b\000c\000\000", 0, 10);
-        x2s("\000.\000\134\000w\000b\000\134\000W\000.\000.\000c\000\000", "\000a\000b\000b\000 \000b\000c\000c\000\000", 0, 14);
-        x2s("\000\134\000s\000\134\000w\000z\000z\000z\000\000", "\000 \000z\000z\000z\000z\000\000", 0, 10);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000.\000\134\000w\000b\000\134\000W\000.\000.\000c\000\000", "\000a\000b\000b\000 \000b\000c\000c\000\000", 0, 14);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000s\000\134\000w\000z\000z\000z\000\000", "\000 \000z\000z\000z\000z\000\000", 0, 10);
         x2s("\000a\000a\000.\000b\000\000", "\000a\000a\000b\000b\000\000", 0, 8);
         ns("\000.\000a\000\000", "\000a\000b\000\000");
         x2s("\000.\000a\000\000", "\000a\000a\000\000", 0, 4);
         x2s("\000^\000a\000\000", "\000a\000\000", 0, 2);
         x2s("\000^\000a\000$\000\000", "\000a\000\000", 0, 2);
-        x2s("\000^\000\134\000w\000$\000\000", "\000a\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000^\000\134\000w\000$\000\000", "\000a\000\000", 0, 2);
         ns("\000^\000\134\000w\000$\000\000", "\000 \000\000");
-        x2s("\000^\000\134\000w\000a\000b\000$\000\000", "\000z\000a\000b\000\000", 0, 6);
-        x2s("\000^\000\134\000w\000a\000b\000c\000d\000e\000f\000$\000\000", "\000z\000a\000b\000c\000d\000e\000f\000\000", 0, 14);
-        x2s("\000^\000\134\000w\000.\000.\000.\000d\000e\000f\000$\000\000", "\000z\000a\000b\000c\000d\000e\000f\000\000", 0, 14);
-        x2s("\000\134\000w\000\134\000w\000\134\000s\000\134\000W\000a\000a\000a\000\134\000d\000\000", "\000a\000a\000 \000 \000a\000a\000a\0004\000\000", 0, 16);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000^\000\134\000w\000a\000b\000$\000\000", "\000z\000a\000b\000\000", 0, 6);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000^\000\134\000w\000a\000b\000c\000d\000e\000f\000$\000\000", "\000z\000a\000b\000c\000d\000e\000f\000\000", 0, 14);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000^\000\134\000w\000.\000.\000.\000d\000e\000f\000$\000\000", "\000z\000a\000b\000c\000d\000e\000f\000\000", 0, 14);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000w\000\134\000w\000\134\000s\000\134\000W\000a\000a\000a\000\134\000d\000\000", "\000a\000a\000 \000 \000a\000a\000a\0004\000\000", 0, 16);
         x2s("\000\134\000A\000\134\000Z\000\000", "\000\000", 0, 0);
         x2s("\000\134\000A\000x\000y\000z\000\000", "\000x\000y\000z\000\000", 0, 6);
         x2s("\000x\000y\000z\000\134\000Z\000\000", "\000x\000y\000z\000\000", 0, 6);
@@ -202,8 +202,8 @@ public class TestU extends Test {
         x2s("\000\134\000^\000\134\000$\000\000", "\000^\000$\000\000", 0, 4);
         x2s("\000^\000x\000?\000y\000\000", "\000x\000y\000\000", 0, 4);
         x2s("\000^\000(\000x\000?\000y\000)\000\000", "\000x\000y\000\000", 0, 4);
-        x2s("\000\134\000w\000\000", "\000_\000\000", 0, 2);
-        ns("\000\134\000W\000\000", "\000_\000\000");
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000w\000\000", "\000_\000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\000\134\000W\000\000", "\000_\000\000");
         x2s("\000(\000?\000=\000z\000)\000z\000\000", "\000z\000\000", 0, 2);
         ns("\000(\000?\000=\000z\000)\000.\000\000", "\000a\000\000");
         x2s("\000(\000?\000!\000z\000)\000a\000\000", "\000a\000\000", 0, 2);
@@ -278,7 +278,7 @@ public class TestU extends Test {
         x2s("\000a\000|\000b\000\134\000Z\000\000", "\000b\000\000", 0, 2);
         x2s("\000a\000|\000b\000\134\000z\000\000", "\000b\000a\000\000", 2, 4);
         x2s("\000a\000|\000b\000\134\000z\000\000", "\000b\000\000", 0, 2);
-        x2s("\000\134\000w\000|\000\134\000s\000\000", "\000 \000\000", 0, 2);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000w\000|\000\134\000s\000\000", "\000 \000\000", 0, 2);
         ns("\000\134\000w\000|\000\134\000w\000\000", "\000 \000\000");
         x2s("\000\134\000w\000|\000%\000\000", "\000%\000\000", 0, 2);
         x2s("\000\134\000w\000|\000[\000&\000$\000]\000\000", "\000&\000\000", 0, 2);
@@ -360,7 +360,7 @@ public class TestU extends Test {
         x2s("\000(\000?\000:\000X\000*\000)\000(\000?\000i\000:\000x\000a\000)\000\000", "\000X\000X\000X\000a\000\000", 0, 8);
         x2s("\000(\000d\000+\000)\000(\000[\000^\000a\000b\000c\000]\000z\000)\000\000", "\000d\000d\000d\000z\000\000", 0, 8);
         x2s("\000(\000[\000^\000a\000b\000c\000]\000*\000)\000(\000[\000^\000a\000b\000c\000]\000z\000)\000\000", "\000d\000d\000d\000z\000\000", 0, 8);
-        x2s("\000(\000\134\000w\000+\000)\000(\000\134\000w\000z\000)\000\000", "\000d\000d\000d\000z\000\000", 0, 8);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000(\000\134\000w\000+\000)\000(\000\134\000w\000z\000)\000\000", "\000d\000d\000d\000z\000\000", 0, 8);
         x3s("\000(\000a\000)\000\000", "\000a\000\000", 0, 2, 1);
         x3s("\000(\000a\000b\000)\000\000", "\000a\000b\000\000", 0, 4, 1);
         x2s("\000(\000(\000a\000b\000)\000)\000\000", "\000a\000b\000\000", 0, 4);
@@ -423,7 +423,7 @@ public class TestU extends Test {
         x3s("\000(\000(\000(\000(\000(\000(\000(\000a\000*\000)\000b\000)\000)\000)\000)\000)\000)\000c\000\134\0007\000\000", "\000a\000a\000a\000b\000c\000a\000a\000a\000\000", 0, 6, 7);
         x2s("\000(\000a\000)\000(\000b\000)\000(\000c\000)\000\134\0002\000\134\0001\000\134\0003\000\000", "\000a\000b\000c\000b\000a\000c\000\000", 0, 12);
         x2s("\000(\000[\000a\000-\000d\000]\000)\000\134\0001\000\000", "\000c\000c\000\000", 0, 4);
-        x2s("\000(\000\134\000w\000\134\000d\000\134\000s\000)\000\134\0001\000\000", "\000f\0005\000 \000f\0005\000 \000\000", 0, 12);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000(\000\134\000w\000\134\000d\000\134\000s\000)\000\134\0001\000\000", "\000f\0005\000 \000f\0005\000 \000\000", 0, 12);
         ns("\000(\000\134\000w\000\134\000d\000\134\000s\000)\000\134\0001\000\000", "\000f\0005\000 \000f\0005\000\000");
         x2s("\000(\000w\000h\000o\000|\000[\000a\000-\000c\000]\000{\0003\000}\000)\000\134\0001\000\000", "\000w\000h\000o\000w\000h\000o\000\000", 0, 12);
         x2s("\000.\000.\000.\000(\000w\000h\000o\000|\000[\000a\000-\000c\000]\000{\0003\000}\000)\000\134\0001\000\000", "\000a\000b\000c\000w\000h\000o\000w\000h\000o\000\000", 0, 18);
@@ -435,7 +435,7 @@ public class TestU extends Test {
         x2s("\000(\000a\000*\000\134\000Z\000)\000\134\0001\000\000", "\000a\000\000", 2, 2);
         x2s("\000.\000(\000a\000*\000\134\000Z\000)\000\134\0001\000\000", "\000b\000a\000\000", 2, 4);
         x3s("\000(\000.\000(\000a\000b\000c\000)\000\134\0002\000)\000\000", "\000z\000a\000b\000c\000a\000b\000c\000\000", 0, 14, 1);
-        x3s("\000(\000.\000(\000.\000.\000\134\000d\000.\000)\000\134\0002\000)\000\000", "\000z\0001\0002\0003\0004\0001\0002\0003\0004\000\000", 0, 18, 1);
+        if (!org.joni.Config.NON_UNICODE_SDW) x3s("\000(\000.\000(\000.\000.\000\134\000d\000.\000)\000\134\0002\000)\000\000", "\000z\0001\0002\0003\0004\0001\0002\0003\0004\000\000", 0, 18, 1);
         x2s("\000(\000(\000?\000i\000:\000a\000z\000)\000)\000\134\0001\000\000", "\000A\000z\000A\000z\000\000", 0, 8);
         ns("\000(\000(\000?\000i\000:\000a\000z\000)\000)\000\134\0001\000\000", "\000A\000z\000a\000z\000\000");
         x2s("\000(\000?\000<\000=\000a\000)\000b\000\000", "\000a\000b\000\000", 2, 4);
@@ -459,10 +459,10 @@ public class TestU extends Test {
         x2s("\000\134\000g\000<\000n\000>\000(\000a\000b\000c\000|\000d\000f\000(\000?\000<\000n\000>\000.\000Y\000Z\000)\000{\0002\000,\0008\000}\000)\000{\0000\000}\000\000", "\000X\000Y\000Z\000\000", 0, 6);
         x2s("\000\134\000A\000(\000?\000<\000n\000>\000(\000a\000\134\000g\000<\000n\000>\000)\000|\000)\000\134\000z\000\000", "\000a\000a\000a\000a\000\000", 0, 8);
         x2s("\000(\000?\000<\000n\000>\000|\000\134\000g\000<\000m\000>\000\134\000g\000<\000n\000>\000)\000\134\000z\000|\000\134\000z\000E\000N\000D\000 \000(\000?\000<\000m\000>\000a\000|\000(\000b\000)\000\134\000g\000<\000m\000>\000)\000\000", "\000b\000b\000b\000b\000a\000b\000b\000a\000\000", 0, 16);
-        x2s("\000(\000?\000<\000n\000a\000m\000e\0001\0002\0004\0000\000>\000\134\000w\000+\000\134\000s\000x\000)\000a\000+\000\134\000k\000<\000n\000a\000m\000e\0001\0002\0004\0000\000>\000\000", "\000 \000 \000f\000g\000 \000x\000a\000a\000a\000a\000a\000a\000a\000a\000f\000g\000 \000x\000\000", 4, 36);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000(\000?\000<\000n\000a\000m\000e\0001\0002\0004\0000\000>\000\134\000w\000+\000\134\000s\000x\000)\000a\000+\000\134\000k\000<\000n\000a\000m\000e\0001\0002\0004\0000\000>\000\000", "\000 \000 \000f\000g\000 \000x\000a\000a\000a\000a\000a\000a\000a\000a\000f\000g\000 \000x\000\000", 4, 36);
         x3s("\000(\000z\000)\000(\000)\000(\000)\000(\000?\000<\000_\0009\000>\000a\000)\000\134\000g\000<\000_\0009\000>\000\000", "\000z\000a\000a\000\000", 4, 6, 1);
         x2s("\000(\000.\000)\000(\000(\000(\000?\000<\000_\000>\000a\000)\000)\000)\000\134\000k\000<\000_\000>\000\000", "\000z\000a\000a\000\000", 0, 6);
-        x2s("\000(\000(\000?\000<\000n\000a\000m\000e\0001\000>\000\134\000d\000)\000|\000(\000?\000<\000n\000a\000m\000e\0002\000>\000\134\000w\000)\000)\000(\000\134\000k\000<\000n\000a\000m\000e\0001\000>\000|\000\134\000k\000<\000n\000a\000m\000e\0002\000>\000)\000\000", "\000f\000f\000\000", 0, 4);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000(\000(\000?\000<\000n\000a\000m\000e\0001\000>\000\134\000d\000)\000|\000(\000?\000<\000n\000a\000m\000e\0002\000>\000\134\000w\000)\000)\000(\000\134\000k\000<\000n\000a\000m\000e\0001\000>\000|\000\134\000k\000<\000n\000a\000m\000e\0002\000>\000)\000\000", "\000f\000f\000\000", 0, 4);
         x2s("\000(\000?\000:\000(\000?\000<\000x\000>\000)\000|\000(\000?\000<\000x\000>\000e\000f\000g\000)\000)\000\134\000k\000<\000x\000>\000\000", "\000\000", 0, 0);
         x2s("\000(\000?\000:\000(\000?\000<\000x\000>\000a\000b\000c\000)\000|\000(\000?\000<\000x\000>\000e\000f\000g\000)\000)\000\134\000k\000<\000x\000>\000\000", "\000a\000b\000c\000e\000f\000g\000e\000f\000g\000\000", 6, 18);
         ns("\000(\000?\000:\000(\000?\000<\000x\000>\000a\000b\000c\000)\000|\000(\000?\000<\000x\000>\000e\000f\000g\000)\000)\000\134\000k\000<\000x\000>\000\000", "\000a\000b\000c\000e\000f\000g\000\000");
@@ -501,7 +501,7 @@ public class TestU extends Test {
         x2s("\000.\000.\000\000", "0K0M\000\000", 0, 4);
         x2s("\000\134\000w\000\000", "0J\000\000", 0, 2);
         ns("\000\134\000W\000\000", "0B\000\000");
-        x2s("\000[\000\134\000W\000]\000\000", "0F\000$\000\000", 2, 4);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000[\000\134\000W\000]\000\000", "0F\000$\000\000", 2, 4);
         x2s("\000\134\000S\000\000", "0]\000\000", 0, 2);
         x2s("\000\134\000S\000\000", "o\042\000\000", 0, 2);
         x2s("\000\134\000b\000\000", "l\027\000 \000\000", 0, 0);
@@ -514,7 +514,7 @@ public class TestU extends Test {
         x2s("\000[0F\000-0J\000]\000\000", "0H\000\000", 0, 2);
         ns("\000[\000^0Q\000]\000\000", "0Q\000\000");
         x2s("\000[\000\134\000w\000]\000\000", "0m\000\000", 0, 2);
-        ns("\000[\000\134\000d\000]\000\000", "0u\000\000");
+        if (!org.joni.Config.NON_UNICODE_SDW) ns("\000[\000\134\000d\000]\000\000", "0u\000\000");
         x2s("\000[\000\134\000D\000]\000\000", "0o\000\000", 0, 2);
         ns("\000[\000\134\000s\000]\000\000", "0O\000\000");
         x2s("\000[\000\134\000S\000]\000\000", "0x\000\000", 0, 2);
@@ -524,16 +524,16 @@ public class TestU extends Test {
         x2s("\233<\000\134\000W\216\312\000\000", "\233<\000 \216\312\000\000", 0, 6);
         x2s("0B\000.0D\000.0F\000\000", "0B0B0D0D0F\000\000", 0, 10);
         x2s("\000.\000\134\000w0F\000\134\000W\000.\000.0^\000\000", "0H0F0F\000 0F0^0^\000\000", 0, 14);
-        x2s("\000\134\000s\000\134\000w0S0S0S\000\000", "\000 0S0S0S0S\000\000", 0, 10);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000s\000\134\000w0S0S0S\000\000", "\000 0S0S0S0S\000\000", 0, 10);
         x2s("0B0B\000.0Q\000\000", "0B0B0Q0Q\000\000", 0, 8);
         ns("\000.0D\000\000", "0D0H\000\000");
         x2s("\000.0J\000\000", "0J0J\000\000", 0, 4);
         x2s("\000^0B\000\000", "0B\000\000", 0, 2);
         x2s("\000^0\200\000$\000\000", "0\200\000\000", 0, 2);
         x2s("\000^\000\134\000w\000$\000\000", "0k\000\000", 0, 2);
-        x2s("\000^\000\134\000w0K0M0O0Q0S\000$\000\000", "\000z0K0M0O0Q0S\000\000", 0, 12);
-        x2s("\000^\000\134\000w\000.\000.\000.0F0H0J\000$\000\000", "\000z0B0D0F0F0H0J\000\000", 0, 14);
-        x2s("\000\134\000w\000\134\000w\000\134\000s\000\134\000W0J0J0J\000\134\000d\000\000", "\000a0J\000 \000 0J0J0J\0004\000\000", 0, 16);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000^\000\134\000w0K0M0O0Q0S\000$\000\000", "\000z0K0M0O0Q0S\000\000", 0, 12);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000^\000\134\000w\000.\000.\000.0F0H0J\000$\000\000", "\000z0B0D0F0F0H0J\000\000", 0, 14);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000\134\000w\000\134\000w\000\134\000s\000\134\000W0J0J0J\000\134\000d\000\000", "\000a0J\000 \000 0J0J0J\0004\000\000", 0, 16);
         x2s("\000\134\000A0_0a0d\000\000", "0_0a0d\000\000", 0, 6);
         x2s("0\2000\2010\202\000\134\000Z\000\000", "0\2000\2010\202\000\000", 0, 6);
         x2s("0K0M0O\000\134\000z\000\000", "0K0M0O\000\000", 0, 6);
@@ -722,7 +722,7 @@ public class TestU extends Test {
         x3s("\000(\000(\000(\000(\000(\000(\000(0}\000*\000)0z\000)\000)\000)\000)\000)\000)0t\000\134\0007\000\000", "0}0}0}0z0t0}0}0}\000\000", 0, 6, 7);
         x2s("\000(0o\000)\000(0r\000)\000(0u\000)\000\134\0002\000\134\0001\000\134\0003\000\000", "0o0r0u0r0o0u\000\000", 0, 12);
         x2s("\000(\000[0M\000-0Q\000]\000)\000\134\0001\000\000", "0O0O\000\000", 0, 4);
-        x2s("\000(\000\134\000w\000\134\000d\000\134\000s\000)\000\134\0001\000\000", "0B\0005\000 0B\0005\000 \000\000", 0, 12);
+        if (!org.joni.Config.NON_UNICODE_SDW) x2s("\000(\000\134\000w\000\134\000d\000\134\000s\000)\000\134\0001\000\000", "0B\0005\000 0B\0005\000 \000\000", 0, 12);
         ns("\000(\000\134\000w\000\134\000d\000\134\000s\000)\000\134\0001\000\000", "0B\0005\000 0B\0005\000\000");
         x2s("\000(\212\260\377\037\000|\000[0B\000-0F\000]\000{\0003\000}\000)\000\134\0001\000\000", "\212\260\377\037\212\260\377\037\000\000", 0, 8);
         x2s("\000.\000.\000.\000(\212\260\377\037\000|\000[0B\000-0F\000]\000{\0003\000}\000)\000\134\0001\000\000", "0B\000a0B\212\260\377\037\212\260\377\037\000\000", 0, 14);
@@ -734,7 +734,7 @@ public class TestU extends Test {
         x2s("\000(0B\000*\000\134\000Z\000)\000\134\0001\000\000", "0B\000\000", 2, 2);
         x2s("\000.\000(0B\000*\000\134\000Z\000)\000\134\0001\000\000", "0D0B\000\000", 2, 4);
         x3s("\000(\000.\000(0\2040D0\206\000)\000\134\0002\000)\000\000", "\000z0\2040D0\2060\2040D0\206\000\000", 0, 14, 1);
-        x3s("\000(\000.\000(\000.\000.\000\134\000d\000.\000)\000\134\0002\000)\000\000", "0B\0001\0002\0003\0004\0001\0002\0003\0004\000\000", 0, 18, 1);
+        if (!org.joni.Config.NON_UNICODE_SDW) x3s("\000(\000.\000(\000.\000.\000\134\000d\000.\000)\000\134\0002\000)\000\000", "0B\0001\0002\0003\0004\0001\0002\0003\0004\000\000", 0, 18, 1);
         x2s("\000(\000(\000?\000i\000:0B\000v0Z\000)\000)\000\134\0001\000\000", "0B\000v0Z0B\000v0Z\000\000", 0, 12);
         x2s("\000(\000?\000<a\0320K\000>Y\011\000|\000\134\000(\000\134\000g\000<a\0320K\000>\000\134\000)\000)\000\000", "\000(\000(\000(\000(\000(\000(Y\011\000)\000)\000)\000)\000)\000)\000\000", 0, 26);
         x2s("\000\134\000A\000(\000?\000:\000\134\000g\000<\226?\000_\0001\000>\000|\000\134\000g\000<N\221\000_\0002\000>\000|\000\134\000z}BN\206\000 \000 \000(\000?\000<\226?\000_\0001\000>\211\263\000|\201\352\000\134\000g\000<N\221\000_\0002\000>\201\352\000)\000(\000?\000<N\221\000_\0002\000>W(\000|\203\351\205\251\000\134\000g\000<\226?\000_\0001\000>\203\351\205\251\000)\000)\000$\000\000", "\203\351\205\251\201\352\203\351\205\251\201\352W(\201\352\203\351\205\251\201\352\203\35 [...]
@@ -763,7 +763,7 @@ public class TestU extends Test {
         x2s("\000a\000<\000b\000>0\3200\3740\2700\3470\3630n0\3000\2460\3630\3550\3740\311\000<\000\134\000/\000b\000>\000\000", "\000a\000<\000b\000>0\3200\3740\2700\3470\3630n0\3000\2460\3630\3550\3740\311\000<\000/\000b\000>\000\000", 0, 40);
         x2s("\000.\000<\000b\000>0\3200\3740\2700\3470\3630n0\3000\2460\3630\3550\3740\311\000<\000\134\000/\000b\000>\000\000", "\000a\000<\000b\000>0\3200\3740\2700\3470\3630n0\3000\2460\3630\3550\3740\311\000<\000/\000b\000>\000\000", 0, 40);
     }
-    
+
     public static void main(String[] args) throws Throwable {
         new TestU().run();
     }
diff --git a/test/org/joni/test/TestU8.java b/test/org/joni/test/TestU8.java
new file mode 100644
index 0000000..53400f6
--- /dev/null
+++ b/test/org/joni/test/TestU8.java
@@ -0,0 +1,84 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.joni.test;
+
+import org.jcodings.Encoding;
+import org.jcodings.specific.UTF8Encoding;
+import org.joni.Option;
+import org.joni.Syntax;
+
+public class TestU8 extends Test {
+
+    public int option() {
+        return Option.DEFAULT;
+    }
+
+    public Encoding encoding() {
+        return UTF8Encoding.INSTANCE;
+    }
+
+    public String testEncoding() {
+        return "iso-8859-1";
+    }
+
+    public Syntax syntax() {
+        return Syntax.DEFAULT;
+    }
+
+    public void test() {
+        xx("^\\d\\d\\d-".getBytes(), new byte []{-30, -126, -84, 48, 45}, 0, 0, 0, true);
+        x2s("x{2}", "xx", 0, 2, Option.IGNORECASE);
+        x2s("x{2}", "XX", 0, 2, Option.IGNORECASE);
+        x2s("x{3}", "XxX", 0, 3, Option.IGNORECASE);
+        ns("x{2}", "x", Option.IGNORECASE);
+        ns("x{2}", "X", Option.IGNORECASE);
+
+        byte[] pat = new byte[] {(byte)227, (byte)131, (byte)160, (byte)40, (byte)46, (byte)41};
+        byte[] str = new byte[]{(byte)227, (byte)130, (byte)185, (byte)227, (byte)131, (byte)145, (byte)227, (byte)131, (byte)160, (byte)227, (byte)131, (byte)143, (byte)227, (byte)131, (byte)179, (byte)227, (byte)130, (byte)175};
+
+        x2(pat, str, 6, 12);
+
+        x2s("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 35, Option.IGNORECASE);
+        x2s("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 0, 35, Option.IGNORECASE);
+        x2s("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAA", 0, 35, Option.IGNORECASE);
+
+        pat = new byte[]{94, 40, (byte)239, (byte)188, (byte)161, 41, 92, 49, 36};
+        str = new byte[]{(byte)239, (byte)188, (byte)161, 65};
+
+        n(pat, str, Option.IGNORECASE);
+
+        pat = new byte[]{94, (byte)195, (byte)159, 123, 50, 125, 36};
+        str = new byte[]{(byte)195, (byte)159, 115, 115};
+
+        x2(pat, str, 0, 4, Option.IGNORECASE);
+
+        String str2 = new String(new byte[]{-61, -123, -61, -123});
+        String pat2 = new String(new byte[]{'^', -61, -123, '{', '2', '}', '$'});
+
+        // x2s(pat2, str2, 4, 4);
+        // x2s(pat2, str2, 4, 4, Option.IGNORECASE);
+
+        ns("(?i-mx:ak)a", "ema");
+    }
+
+    public static void main(String[] args) throws Throwable {
+        new TestU8().run();
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jruby-joni.git



More information about the pkg-java-commits mailing list