Merged master into issue/SLING-7681

* brought in fixes for SLING-7710, SLING-7549
diff --git a/pom.xml b/pom.xml
index ce2529a..6e9b88c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,7 @@
         The versioning scheme defined here corresponds to SLING-7406 (<module_version>-<htl_specification_version>). Take care when
         releasing to only increase the first part, unless the module provides support for a newer version of the HTL specification.
     -->
-    <version>1.0.21-1.3.1-SNAPSHOT</version>
+    <version>1.0.21-1.4.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
     <name>Apache Sling Scripting HTL Compiler</name>
@@ -85,7 +85,8 @@
                             io.sightly.compiler; version:Version=1.1,
                             io.sightly.compiler; version:Version=1.2,
                             io.sightly.compiler; version:Version=1.3,
-                            io.sightly.compiler; version:Version=1.3.1
+                            io.sightly.compiler; version:Version=1.3.1,
+                            io.sightly.compiler; version:Version=1.4.0
                         </Provide-Capability>
                     </instructions>
                 </configuration>
diff --git a/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyLexer.g4 b/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyLexer.g4
index 9dc6ebf..429dfb4 100644
--- a/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyLexer.g4
+++ b/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyLexer.g4
@@ -38,6 +38,8 @@
 
 RBRACKET: ')';
 
+IN_OP: 'in';
+
 AND_OP: '&&';
 
 OR_OP: '||';
@@ -72,16 +74,17 @@
 
 // tokens
 
-ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|':')*
+ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|':')*
     ;
 
-INT :	'0'..'9'+
+INT : '-'? '1'..'9'+ '0'..'9'*
+    | '0'
     ;
 
 FLOAT
-    :   ('0'..'9')+ '.' ('0'..'9')+ EXPONENT?
-//    |   '.' ('0'..'9')+ EXPONENT?  --> conflicts with a.2 notation
-    |   ('0'..'9')+ EXPONENT
+    :   '-'? '1'..'9'+ '0'..'9'* '.' '0'..'9'+ EXPONENT?
+    |   '-'? '0.' '0'..'9'+ EXPONENT?
+    |   '-'? '1'..'9'+ '0'..'9'* EXPONENT
     ;
 
 COMMENT: '<!--/*' .*? '*/-->' -> skip;
@@ -113,7 +116,7 @@
 
 fragment
 ESC_SEQ
-    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
+    :   '\\' ('b'|'t'|'n'|'f'|'r'|'"'|'\''|'\\')
     |   UNICODE_ESC
     ;
 
diff --git a/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyParser.g4 b/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyParser.g4
index 81d511a..e605fca 100644
--- a/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyParser.g4
+++ b/src/main/antlr4/org/apache/sling/scripting/sightly/impl/parser/expr/generated/SightlyParser.g4
@@ -72,18 +72,21 @@
 
 
 exprNode returns [ExpressionNode node]
-    :   condition=binaryOp TERNARY_Q_OP thenBranch=binaryOp TERNARY_BRANCHES_OP elseBranch=binaryOp
+    :   condition=orBinaryOp TERNARY_Q_OP thenBranch=orBinaryOp TERNARY_BRANCHES_OP elseBranch=orBinaryOp
         {$node = new TernaryOperator($condition.node, $thenBranch.node, $elseBranch.node);}
-    |   binaryOp {$node = $binaryOp.node;}
+    |   orBinaryOp {$node = $orBinaryOp.node;}
     ;
 
-binaryOp returns [ExpressionNode node] //is there any priority precedence between AND & OR ?
-    :   left=comparisonTerm { $node = $left.node; }
-        (operator right=comparisonTerm { $node = new BinaryOperation($operator.op, $node, $right.node); })*
+orBinaryOp returns [ExpressionNode node]
+    : left=andBinaryOp { $node = $left.node; } (OR_OP right=andBinaryOp { $node = new BinaryOperation(BinaryOperator.OR, $node, $right.node);})*
     ;
-    
-operator returns [BinaryOperator op]
-    :    AND_OP { $op = BinaryOperator.AND; } | OR_OP { $op = BinaryOperator.OR; }
+
+andBinaryOp returns [ExpressionNode node]
+    : left=inBinaryOp { $node = $left.node; } (AND_OP right=inBinaryOp{ $node = new BinaryOperation(BinaryOperator.AND, $node, $right.node);})*
+    ;
+
+inBinaryOp returns [ExpressionNode node]
+    : left=comparisonTerm { $node = $left.node; } (IN_OP right=comparisonTerm { $node = new BinaryOperation(BinaryOperator.IN, $node, $right.node); })*
     ;
 
 comparisonTerm returns [ExpressionNode node]
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/SightlyCompiler.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/SightlyCompiler.java
index 7c14e0e..6aeecde 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/SightlyCompiler.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/SightlyCompiler.java
@@ -54,6 +54,7 @@
 import org.apache.sling.scripting.sightly.impl.plugin.Plugin;
 import org.apache.sling.scripting.sightly.impl.plugin.RepeatPlugin;
 import org.apache.sling.scripting.sightly.impl.plugin.ResourcePlugin;
+import org.apache.sling.scripting.sightly.impl.plugin.SetPlugin;
 import org.apache.sling.scripting.sightly.impl.plugin.TemplatePlugin;
 import org.apache.sling.scripting.sightly.impl.plugin.TestPlugin;
 import org.apache.sling.scripting.sightly.impl.plugin.TextPlugin;
@@ -96,6 +97,7 @@
         plugins.add(new ResourcePlugin());
         plugins.add(new TemplatePlugin());
         plugins.add(new TestPlugin());
+        plugins.add(new SetPlugin());
         plugins.add(new TextPlugin());
         plugins.add(new UnwrapPlugin());
         plugins.add(new UsePlugin());
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/Loop.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/Loop.java
index 74dd016..4b3cc8c 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/Loop.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/Loop.java
@@ -28,11 +28,21 @@
         private String listVariable;
         private String itemVariable;
         private String indexVariable;
+        private String beginVariable;
+        private String stepVariable;
+        private String endVariable;
 
         public Start(String listVariable, String itemVariable, String indexVariable) {
+            this(listVariable, itemVariable, indexVariable, null, null, null);
+        }
+
+        public Start(String listVariable, String itemVariable, String indexVariable, String beginVariable, String stepVariable, String endVariable) {
             this.listVariable = listVariable;
             this.itemVariable = itemVariable;
             this.indexVariable = indexVariable;
+            this.beginVariable = beginVariable;
+            this.stepVariable = stepVariable;
+            this.endVariable = endVariable;
         }
 
         public String getListVariable() {
@@ -47,6 +57,18 @@
             return indexVariable;
         }
 
+        public String getBeginVariable() {
+            return beginVariable;
+        }
+
+        public String getStepVariable() {
+            return stepVariable;
+        }
+
+        public String getEndVariable() {
+            return endVariable;
+        }
+
         @Override
         public void accept(CommandVisitor visitor) {
             visitor.visit(this);
@@ -58,6 +80,9 @@
                     "listVariable='" + listVariable + '\'' +
                     ", itemVariable='" + itemVariable + '\'' +
                     ", indexVariable='" + indexVariable + '\'' +
+                    ", beginVariable='" + beginVariable + '\'' +
+                    ", stepVariable='" + stepVariable + '\'' +
+                    ", endVariable='" + endVariable + '\'' +
                     '}';
         }
     }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/package-info.java
index dd2f081..7656a51 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/package-info.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/commands/package-info.java
@@ -21,7 +21,7 @@
  * The {@code org.apache.sling.scripting.sightly.compiler.commands} package defines the API for
  * {@link org.apache.sling.scripting.sightly.compiler.commands.Command} processing.
  */
-@Version("1.0.0")
+@Version("1.1.0")
 package org.apache.sling.scripting.sightly.compiler.commands;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java
index d689093..ff6bb9a 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java
@@ -18,6 +18,8 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.compiler.expression.nodes;
 
+import java.util.Collection;
+
 import org.apache.sling.scripting.sightly.compiler.SightlyCompilerException;
 import org.apache.sling.scripting.sightly.compiler.util.ObjectModel;
 
@@ -182,6 +184,13 @@
                     % ObjectModel.toNumber(right).intValue());
         }
 
+    },
+
+    IN {
+        @Override
+        public Object eval(Object left, Object right) {
+            return inOp(left, right);
+        }
     };
 
     public static boolean eq(Object left, Object right) {
@@ -239,6 +248,21 @@
                 " and Boolean types.");
     }
 
+    public static boolean inOp(Object left, Object right) {
+        if (left instanceof String && right instanceof String) {
+            String leftString = (String) left;
+            String rightString = (String) right;
+            return rightString.contains(leftString);
+        }
+        Collection rightElements = ObjectModel.toCollection(right);
+        for (Object element : rightElements) {
+            if (element.equals(left)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static Number adjust(double x) {
         if (Math.floor(x) == x) {
             return (int) x;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/NumericConstant.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/NumericConstant.java
index d03056c..cee38a0 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/NumericConstant.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/NumericConstant.java
@@ -80,8 +80,12 @@
     }
 
     private Number parseNumber(String s) {
-        if (s.contains(".")) {
-            return Double.parseDouble(s);
+        if (s.contains(".") || s.contains("e") || s.contains("E")) {
+            double interim = Double.parseDouble(s);
+            if (interim == 0) {
+                return 0.0;
+            }
+            return interim;
         }
         return Long.parseLong(s);
     }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java
index 69398b9..104dfe7 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java
@@ -19,7 +19,7 @@
  * The {@code org.apache.sling.scripting.sightly.compiler.expression.nodes} package exposes the various
  * {@link org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode} types.
  */
-@Version("1.0.0")
+@Version("1.1.0")
 package org.apache.sling.scripting.sightly.compiler.expression.nodes;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/util/VariableTracker.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/util/VariableTracker.java
index 54c4189..1157486 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/util/VariableTracker.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/util/VariableTracker.java
@@ -50,14 +50,16 @@
      * @param data the data associated with the variable
      */
     public void pushVariable(String name, T data) {
-        name = name.toLowerCase();
-        Stack<T> dataStack = variableData.get(name);
-        if (dataStack == null) {
-            dataStack = new Stack<>();
-            variableData.put(name, dataStack);
+        if (name != null) {
+            name = name.toLowerCase();
+            Stack<T> dataStack = variableData.get(name);
+            if (dataStack == null) {
+                dataStack = new Stack<>();
+                variableData.put(name, dataStack);
+            }
+            dataStack.push(data);
+            declarationStack.push(name);
         }
-        dataStack.push(data);
-        declarationStack.push(name);
     }
 
     /**
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/visitor/TrackingVisitor.java b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/visitor/TrackingVisitor.java
index 246a8c9..2f773d9 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/visitor/TrackingVisitor.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/visitor/TrackingVisitor.java
@@ -47,6 +47,9 @@
         super.visit(loopStart);
         tracker.pushVariable(loopStart.getIndexVariable(), assignDefault(loopStart));
         tracker.pushVariable(loopStart.getItemVariable(), assignDefault(loopStart));
+        tracker.pushVariable(loopStart.getBeginVariable(), assignDefault(loopStart));
+        tracker.pushVariable(loopStart.getStepVariable(), assignDefault(loopStart));
+        tracker.pushVariable(loopStart.getEndVariable(), assignDefault(loopStart));
     }
 
     @Override
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/ExpressionContext.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/ExpressionContext.java
index b9cc06c..531e813 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/ExpressionContext.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/ExpressionContext.java
@@ -30,6 +30,7 @@
     PLUGIN_DATA_SLY_ATTRIBUTE,
     PLUGIN_DATA_SLY_ELEMENT,
     PLUGIN_DATA_SLY_TEST,
+    PLUGIN_DATA_SLY_SET,
     PLUGIN_DATA_SLY_LIST,
     PLUGIN_DATA_SLY_REPEAT,
     PLUGIN_DATA_SLY_INCLUDE,
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractPlugin.java
index cd09ef7..5a204c1 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractPlugin.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractPlugin.java
@@ -37,4 +37,12 @@
     public int compareTo(Plugin o) {
         return this.priority - o.priority();
     }
+
+    public String decodeVariableName(PluginCallInfo pluginCallInfo) {
+        String[] args = pluginCallInfo.getArguments();
+        if (args.length > 0) {
+            return args[0];
+        }
+        return null;
+    }
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractRepeatPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractRepeatPlugin.java
new file mode 100644
index 0000000..801d4e8
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/AbstractRepeatPlugin.java
@@ -0,0 +1,71 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.scripting.sightly.impl.plugin;
+
+import java.util.HashMap;
+
+import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperation;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.MapLiteral;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.NumericConstant;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperator;
+
+public abstract class AbstractRepeatPlugin extends AbstractPlugin {
+
+    protected static final String INDEX = "index";
+    protected static final String COUNT = "count";
+    protected static final String FIRST = "first";
+    protected static final String MIDDLE = "middle";
+    protected static final String LAST = "last";
+    protected static final String ODD = "odd";
+    protected static final String EVEN = "even";
+    protected static final String BEGIN = "begin";
+    protected static final String STEP = "step";
+    protected static final String END = "end";
+
+    protected MapLiteral buildStatusObj(String indexVar, String sizeVar) {
+        HashMap<String, ExpressionNode> obj = new HashMap<>();
+        Identifier indexId = new Identifier(indexVar);
+        BinaryOperation firstExpr = new BinaryOperation(BinaryOperator.EQ, indexId, NumericConstant.ZERO);
+        BinaryOperation lastExpr = new BinaryOperation(
+                BinaryOperator.EQ,
+                indexId,
+                new BinaryOperation(BinaryOperator.SUB, new Identifier(sizeVar), NumericConstant.ONE));
+        obj.put(INDEX, indexId);
+        obj.put(COUNT, new BinaryOperation(BinaryOperator.ADD, indexId, NumericConstant.ONE));
+        obj.put(FIRST, firstExpr);
+        obj.put(MIDDLE, new UnaryOperation(
+                UnaryOperator.NOT,
+                new BinaryOperation(BinaryOperator.OR, firstExpr, lastExpr)));
+        obj.put(LAST, lastExpr);
+        obj.put(ODD, parityCheck(indexId, NumericConstant.ZERO));
+        obj.put(EVEN, parityCheck(indexId, NumericConstant.ONE));
+        return new MapLiteral(obj);
+    }
+
+    private ExpressionNode parityCheck(ExpressionNode numericExpression, NumericConstant expected) {
+        return new BinaryOperation(
+                BinaryOperator.EQ,
+                new BinaryOperation(BinaryOperator.REM, numericExpression, NumericConstant.TWO),
+                expected);
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ListPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ListPlugin.java
index 6a62c5a..a613a69 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ListPlugin.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ListPlugin.java
@@ -18,33 +18,24 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.plugin;
 
-import java.util.HashMap;
+import java.util.Map;
 
-import org.apache.sling.scripting.sightly.impl.compiler.Syntax;
+import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
+import org.apache.sling.scripting.sightly.compiler.commands.Loop;
+import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding;
 import org.apache.sling.scripting.sightly.compiler.expression.Expression;
 import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperation;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
-import org.apache.sling.scripting.sightly.compiler.expression.nodes.MapLiteral;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.NumericConstant;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperator;
-import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
-import org.apache.sling.scripting.sightly.compiler.commands.Loop;
-import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding;
-import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
 import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
+import org.apache.sling.scripting.sightly.impl.compiler.Syntax;
+import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
 
-public class ListPlugin extends AbstractPlugin {
-
-    private static final String INDEX = "index";
-    private static final String COUNT = "count";
-    private static final String FIRST = "first";
-    private static final String MIDDLE = "middle";
-    private static final String LAST = "last";
-    private static final String ODD = "odd";
-    private static final String EVEN = "even";
+public class ListPlugin extends AbstractRepeatPlugin {
 
     public ListPlugin() {
         name = "list";
@@ -57,14 +48,51 @@
 
             private String listVariable = compilerContext.generateVariable("collectionVar");
             private String collectionSizeVar = compilerContext.generateVariable("size");
+            private String collectionNotEmpty = compilerContext.generateVariable("notEmpty");
+            private String beginVariable = compilerContext.generateVariable(BEGIN);
+            private String stepVariable = compilerContext.generateVariable(STEP);
+            private String endVariable = compilerContext.generateVariable(END);
+            private String validStartStepEnd = compilerContext.generateVariable("validStartStepEnd");
 
             @Override
             public void beforeElement(PushStream stream, String tagName) {
                 stream.write(new VariableBinding.Start(listVariable, expression.getRoot()));
                 stream.write(new VariableBinding.Start(collectionSizeVar,
                         new UnaryOperation(UnaryOperator.LENGTH, new Identifier(listVariable))));
-                stream.write(new Conditional.Start(collectionSizeVar, true));
-
+                stream.write(new VariableBinding.Start(collectionNotEmpty, new BinaryOperation(BinaryOperator.GT, new Identifier
+                        (collectionSizeVar), NumericConstant.ZERO)));
+                stream.write(new Conditional.Start(collectionNotEmpty, true));
+                Map<String, ExpressionNode> options = expression.getOptions();
+                if (options.containsKey(BEGIN)) {
+                    stream.write(new VariableBinding.Start(beginVariable, expression.getOptions().get(BEGIN)));
+                } else {
+                    stream.write(new VariableBinding.Start(beginVariable, NumericConstant.ZERO));
+                }
+                if (options.containsKey(STEP)) {
+                    stream.write(new VariableBinding.Start(stepVariable, expression.getOptions().get(STEP)));
+                } else {
+                    stream.write(new VariableBinding.Start(stepVariable, NumericConstant.ONE));
+                }
+                if (options.containsKey(END)) {
+                    stream.write(new VariableBinding.Start(endVariable, expression.getOptions().get(END)));
+                } else {
+                    stream.write(new VariableBinding.Start(endVariable, new Identifier(collectionSizeVar)));
+                }
+                stream.write(new VariableBinding.Start(validStartStepEnd,
+                            new BinaryOperation(BinaryOperator.AND,
+                                new BinaryOperation(BinaryOperator.AND,
+                                    new BinaryOperation(BinaryOperator.LT, new Identifier(beginVariable), new Identifier(collectionSizeVar)),
+                                    new BinaryOperation(
+                                            BinaryOperator.AND,
+                                            new BinaryOperation(BinaryOperator.GEQ, new Identifier(beginVariable), NumericConstant.ZERO),
+                                            new BinaryOperation(BinaryOperator.GT, new Identifier(stepVariable), NumericConstant.ZERO)
+                                    )
+                                ),
+                                new BinaryOperation(BinaryOperator.GT, new Identifier(endVariable), NumericConstant.ZERO)
+                            )
+                        )
+                );
+                stream.write(new Conditional.Start(validStartStepEnd, true));
             }
 
             @Override
@@ -74,10 +102,39 @@
                 String indexVariable = compilerContext.generateVariable("index");
                 stream.write(new Loop.Start(listVariable, itemVariable, indexVariable));
                 stream.write(new VariableBinding.Start(loopStatusVar, buildStatusObj(indexVariable, collectionSizeVar)));
+                String stepConditionVariable = compilerContext.generateVariable("stepCondition");
+                stream.write(new VariableBinding.Start(stepConditionVariable,
+                        new BinaryOperation(
+                                BinaryOperator.REM,
+                                new BinaryOperation(
+                                        BinaryOperator.SUB,
+                                        new Identifier(indexVariable),
+                                        new Identifier(beginVariable)
+                                ),
+                                new Identifier(stepVariable))
+                        )
+                );
+                String loopTraversalVariable = compilerContext.generateVariable("traversal");
+                stream.write(new VariableBinding.Start(loopTraversalVariable,
+                            new BinaryOperation(
+                                BinaryOperator.AND,
+                                new BinaryOperation(
+                                    BinaryOperator.AND,
+                                    new BinaryOperation(BinaryOperator.GEQ, new Identifier(indexVariable), new Identifier(beginVariable)),
+                                    new BinaryOperation(BinaryOperator.LEQ, new Identifier(indexVariable), new Identifier(endVariable))
+                                ),
+                                new BinaryOperation(BinaryOperator.EQ, new Identifier(stepConditionVariable), NumericConstant.ZERO)
+                            )
+                        )
+                );
+                stream.write(new Conditional.Start(loopTraversalVariable, true));
             }
 
             @Override
             public void afterChildren(PushStream stream) {
+                stream.write(Conditional.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
                 stream.write(VariableBinding.END);
                 stream.write(Loop.END);
             }
@@ -87,6 +144,12 @@
                 stream.write(Conditional.END);
                 stream.write(VariableBinding.END);
                 stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
+                stream.write(Conditional.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
             }
 
 
@@ -97,33 +160,6 @@
                 }
                 return Syntax.DEFAULT_LIST_ITEM_VAR_NAME;
             }
-
-            private MapLiteral buildStatusObj(String indexVar, String sizeVar) {
-                HashMap<String, ExpressionNode> obj = new HashMap<>();
-                Identifier indexId = new Identifier(indexVar);
-                BinaryOperation firstExpr = new BinaryOperation(BinaryOperator.EQ, indexId, NumericConstant.ZERO);
-                BinaryOperation lastExpr = new BinaryOperation(
-                        BinaryOperator.EQ,
-                        indexId,
-                        new BinaryOperation(BinaryOperator.SUB, new Identifier(sizeVar), NumericConstant.ONE));
-                obj.put(INDEX, indexId);
-                obj.put(COUNT, new BinaryOperation(BinaryOperator.ADD, indexId, NumericConstant.ONE));
-                obj.put(FIRST, firstExpr);
-                obj.put(MIDDLE, new UnaryOperation(
-                        UnaryOperator.NOT,
-                        new BinaryOperation(BinaryOperator.OR, firstExpr, lastExpr)));
-                obj.put(LAST, lastExpr);
-                obj.put(ODD, parityCheck(indexId, NumericConstant.ZERO));
-                obj.put(EVEN, parityCheck(indexId, NumericConstant.ONE));
-                return new MapLiteral(obj);
-            }
-
-            private ExpressionNode parityCheck(ExpressionNode numericExpression, NumericConstant expected) {
-                return new BinaryOperation(
-                        BinaryOperator.EQ,
-                        new BinaryOperation(BinaryOperator.REM, numericExpression, NumericConstant.TWO),
-                        expected);
-            }
         };
     }
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/RepeatPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/RepeatPlugin.java
index e8ca678..ff8d41d 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/RepeatPlugin.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/RepeatPlugin.java
@@ -18,34 +18,26 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.plugin;
 
-import java.util.HashMap;
+import java.util.Map;
 
+import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
+import org.apache.sling.scripting.sightly.compiler.commands.Loop;
 import org.apache.sling.scripting.sightly.compiler.commands.OutText;
-import org.apache.sling.scripting.sightly.impl.compiler.Syntax;
+import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding;
 import org.apache.sling.scripting.sightly.compiler.expression.Expression;
 import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperation;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
-import org.apache.sling.scripting.sightly.compiler.expression.nodes.MapLiteral;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.NumericConstant;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperator;
-import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
-import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
-import org.apache.sling.scripting.sightly.compiler.commands.Loop;
-import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding;
 import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
+import org.apache.sling.scripting.sightly.impl.compiler.Syntax;
+import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
 
-public class RepeatPlugin extends AbstractPlugin {
+public class RepeatPlugin extends AbstractRepeatPlugin {
 
-    private static final String INDEX = "index";
-    private static final String COUNT = "count";
-    private static final String FIRST = "first";
-    private static final String MIDDLE = "middle";
-    private static final String LAST = "last";
-    private static final String ODD = "odd";
-    private static final String EVEN = "even";
     private static final OutText NEW_LINE = new OutText("\n");
 
     public RepeatPlugin() {
@@ -59,18 +51,83 @@
 
             private String listVariable = compilerContext.generateVariable("collectionVar");
             private String collectionSizeVar = compilerContext.generateVariable("size");
+            private String collectionNotEmpty = compilerContext.generateVariable("notEmpty");
+            private String beginVariable = compilerContext.generateVariable(BEGIN);
+            private String stepVariable = compilerContext.generateVariable(STEP);
+            private String endVariable = compilerContext.generateVariable(END);
+            private String validStartStepEnd = compilerContext.generateVariable("validStartStepEnd");
 
             @Override
             public void beforeElement(PushStream stream, String tagName) {
                 stream.write(new VariableBinding.Start(listVariable, expression.getRoot()));
                 stream.write(new VariableBinding.Start(collectionSizeVar,
                         new UnaryOperation(UnaryOperator.LENGTH, new Identifier(listVariable))));
-                stream.write(new Conditional.Start(collectionSizeVar, true));
+                stream.write(new VariableBinding.Start(collectionNotEmpty, new BinaryOperation(BinaryOperator.GT, new Identifier
+                        (collectionSizeVar), NumericConstant.ZERO)));
+                stream.write(new Conditional.Start(collectionNotEmpty, true));
+                Map<String, ExpressionNode> options = expression.getOptions();
+                if (options.containsKey(BEGIN)) {
+                    stream.write(new VariableBinding.Start(beginVariable, expression.getOptions().get(BEGIN)));
+                } else {
+                    stream.write(new VariableBinding.Start(beginVariable, NumericConstant.ZERO));
+                }
+                if (options.containsKey(STEP)) {
+                    stream.write(new VariableBinding.Start(stepVariable, expression.getOptions().get(STEP)));
+                } else {
+                    stream.write(new VariableBinding.Start(stepVariable, NumericConstant.ONE));
+                }
+                if (options.containsKey(END)) {
+                    stream.write(new VariableBinding.Start(endVariable, expression.getOptions().get(END)));
+                } else {
+                    stream.write(new VariableBinding.Start(endVariable, new Identifier(collectionSizeVar)));
+                }
+                stream.write(new VariableBinding.Start(validStartStepEnd,
+                                new BinaryOperation(BinaryOperator.AND,
+                                        new BinaryOperation(BinaryOperator.AND,
+                                                new BinaryOperation(BinaryOperator.LT, new Identifier(beginVariable), new Identifier(collectionSizeVar)),
+                                                new BinaryOperation(
+                                                        BinaryOperator.AND,
+                                                        new BinaryOperation(BinaryOperator.GEQ, new Identifier(beginVariable), NumericConstant.ZERO),
+                                                        new BinaryOperation(BinaryOperator.GT, new Identifier(stepVariable), NumericConstant.ZERO)
+                                                )
+                                        ),
+                                        new BinaryOperation(BinaryOperator.GT, new Identifier(endVariable), NumericConstant.ZERO)
+                                )
+                        )
+                );
+                stream.write(new Conditional.Start(validStartStepEnd, true));
                 String itemVariable = decodeItemVariable();
                 String loopStatusVar = Syntax.itemLoopStatusVariable(itemVariable);
                 String indexVariable = compilerContext.generateVariable("index");
                 stream.write(new Loop.Start(listVariable, itemVariable, indexVariable));
                 stream.write(new VariableBinding.Start(loopStatusVar, buildStatusObj(indexVariable, collectionSizeVar)));
+                String stepConditionVariable = compilerContext.generateVariable("stepCondition");
+                stream.write(new VariableBinding.Start(stepConditionVariable,
+                                new BinaryOperation(
+                                        BinaryOperator.REM,
+                                        new BinaryOperation(
+                                                BinaryOperator.SUB,
+                                                new Identifier(indexVariable),
+                                                new Identifier(beginVariable)
+                                        ),
+                                        new Identifier(stepVariable))
+                        )
+                );
+                String loopTraversalVariable = compilerContext.generateVariable("traversal");
+                stream.write(new VariableBinding.Start(loopTraversalVariable,
+                                new BinaryOperation(
+                                        BinaryOperator.AND,
+                                        new BinaryOperation(
+                                                BinaryOperator.AND,
+                                                new BinaryOperation(BinaryOperator.GEQ, new Identifier(indexVariable), new Identifier(
+                                                        beginVariable)),
+                                                new BinaryOperation(BinaryOperator.LEQ, new Identifier(indexVariable), new Identifier(endVariable))
+                                        ),
+                                        new BinaryOperation(BinaryOperator.EQ, new Identifier(stepConditionVariable), NumericConstant.ZERO)
+                                )
+                        )
+                );
+                stream.write(new Conditional.Start(loopTraversalVariable, true));
 
             }
 
@@ -81,11 +138,20 @@
 
             @Override
             public void afterElement(PushStream stream) {
+                stream.write(Conditional.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
                 stream.write(VariableBinding.END);
                 stream.write(Loop.END);
                 stream.write(Conditional.END);
                 stream.write(VariableBinding.END);
                 stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
+                stream.write(Conditional.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
+                stream.write(VariableBinding.END);
             }
 
 
@@ -97,26 +163,6 @@
                 return Syntax.DEFAULT_LIST_ITEM_VAR_NAME;
             }
 
-            private MapLiteral buildStatusObj(String indexVar, String sizeVar) {
-                HashMap<String, ExpressionNode> obj = new HashMap<>();
-                Identifier indexId = new Identifier(indexVar);
-                BinaryOperation firstExpr = new BinaryOperation(BinaryOperator.EQ, indexId, NumericConstant.ZERO);
-                BinaryOperation lastExpr = new BinaryOperation(
-                        BinaryOperator.EQ,
-                        indexId,
-                        new BinaryOperation(BinaryOperator.SUB, new Identifier(sizeVar), NumericConstant.ONE));
-                obj.put(INDEX, indexId);
-                obj.put(COUNT, new BinaryOperation(BinaryOperator.ADD, indexId, NumericConstant.ONE));
-                obj.put(FIRST, firstExpr);
-                obj.put(MIDDLE, new UnaryOperation(
-                        UnaryOperator.NOT,
-                        new BinaryOperation(BinaryOperator.OR, firstExpr, lastExpr)));
-                obj.put(LAST, lastExpr);
-                obj.put(ODD, parityCheck(indexId, NumericConstant.ZERO));
-                obj.put(EVEN, parityCheck(indexId, NumericConstant.ONE));
-                return new MapLiteral(obj);
-            }
-
             private ExpressionNode parityCheck(ExpressionNode numericExpression, NumericConstant expected) {
                 return new BinaryOperation(
                         BinaryOperator.EQ,
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/SetPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/SetPlugin.java
new file mode 100644
index 0000000..f4fe975
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/SetPlugin.java
@@ -0,0 +1,57 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.scripting.sightly.impl.plugin;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.scripting.sightly.compiler.SightlyCompilerException;
+import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding;
+import org.apache.sling.scripting.sightly.compiler.expression.Expression;
+import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
+import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
+
+/**
+ * Implementation for the {@code data-sly-set} plugin
+ */
+public class SetPlugin extends AbstractPlugin {
+
+    public SetPlugin() {
+        name = "set";
+        priority = 1;
+    }
+
+    @Override
+    public PluginInvoke invoke(final Expression expressionNode, final PluginCallInfo callInfo, final CompilerContext compilerContext) {
+
+        final String variableName = decodeVariableName(callInfo);
+        if (StringUtils.isEmpty(variableName)) {
+            throw new SightlyCompilerException("Identifier name was not provided.");
+        }
+
+        return new DefaultPluginInvoke() {
+
+            @Override
+            public void beforeElement(PushStream stream, String tagName) {
+                stream.write(new VariableBinding.Global(variableName, expressionNode.getRoot()));
+            }
+
+
+        };
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/TestPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/TestPlugin.java
index 66570d4..202bfb4 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/TestPlugin.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/TestPlugin.java
@@ -43,7 +43,7 @@
 
             @Override
             public void beforeElement(PushStream stream, String tagName) {
-                String variableName = decodeVariableName();
+                String variableName = decodeVariableName(callInfo);
                 globalBinding = variableName != null;
                 if (variableName == null) {
                     variableName = compilerContext.generateVariable("testVariable");
@@ -63,15 +63,6 @@
                     stream.write(VariableBinding.END);
                 }
             }
-
-            private String decodeVariableName() {
-                String[] args = callInfo.getArguments();
-                if (args.length > 0) {
-                    return args[0];
-                }
-                return null;
-            }
-
         };
     }
 
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/UnwrapPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/UnwrapPlugin.java
index 104ecf7..51e2594 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/UnwrapPlugin.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/UnwrapPlugin.java
@@ -40,17 +40,28 @@
     }
 
     @Override
-    public PluginInvoke invoke(final Expression expression, PluginCallInfo callInfo, final CompilerContext compilerContext) {
+    public PluginInvoke invoke(final Expression expression, final PluginCallInfo callInfo, final CompilerContext compilerContext) {
+
         return new DefaultPluginInvoke() {
 
-            private final String variable = compilerContext.generateVariable("unwrapCondition");
-            private final Command unwrapTest = new Conditional.Start(variable, false);
+            private Command unwrapTest;
             private boolean isSlyTag = false;
+            private String identifierName = decodeVariableName(callInfo);
+            private boolean globalBinding;
 
             @Override
             public void beforeElement(PushStream stream, String tagName) {
+                globalBinding = identifierName != null;
+                if (identifierName == null) {
+                    identifierName = compilerContext.generateVariable("unwrapCondition");
+                }
+                if (globalBinding) {
+                    stream.write(new VariableBinding.Global(identifierName, expression.getRoot()));
+                } else {
+                    stream.write(new VariableBinding.Start(identifierName, testNode()));
+                }
                 isSlyTag = "sly".equals(tagName.toLowerCase());
-                stream.write(new VariableBinding.Start(variable, testNode()));
+                unwrapTest = new Conditional.Start(identifierName, false);
             }
 
             @Override
@@ -87,7 +98,9 @@
 
             @Override
             public void afterElement(PushStream stream) {
-                stream.write(VariableBinding.END);
+                if (!globalBinding) {
+                    stream.write(VariableBinding.END);
+                }
             }
 
             private ExpressionNode testNode() {
diff --git a/src/test/java/org/apache/sling/scripting/sightly/impl/TestUtils.java b/src/test/java/org/apache/sling/scripting/sightly/impl/TestUtils.java
deleted file mode 100644
index bcc014a..0000000
--- a/src/test/java/org/apache/sling/scripting/sightly/impl/TestUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*******************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- ******************************************************************************/
-package org.apache.sling.scripting.sightly.impl;
-
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-import org.apache.sling.scripting.sightly.compiler.CompilationUnit;
-
-/**
- * Testing static utilities that simplify writing tests for the HTL compiler.
- */
-public class TestUtils {
-
-    public static CompilationUnit readScriptFromClasspath(final String scriptResource) {
-        InputStream stream = TestUtils.class.getResourceAsStream(scriptResource);
-        final Reader reader = new InputStreamReader(stream);
-        return new CompilationUnit() {
-            @Override
-            public String getScriptName() {
-                return scriptResource;
-            }
-
-            @Override
-            public Reader getScriptReader() {
-                return reader;
-            }
-        };
-    }
-}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java b/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java
index e861b5f..7d95e1d 100644
--- a/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java
+++ b/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java
@@ -18,13 +18,16 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.compiler;
 
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
 import java.util.List;
 
 import org.apache.sling.scripting.sightly.compiler.CompilationResult;
 import org.apache.sling.scripting.sightly.compiler.CompilationUnit;
 import org.apache.sling.scripting.sightly.compiler.CompilerMessage;
 import org.apache.sling.scripting.sightly.compiler.SightlyCompiler;
-import org.apache.sling.scripting.sightly.impl.TestUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.powermock.api.mockito.PowerMockito;
@@ -42,7 +45,7 @@
 
     @Test
     public void testEmptyExpression() {
-        CompilationResult result = compile("/empty-expression.html");
+        CompilationResult result = compileFile("/empty-expression.html");
         assertTrue("Didn't expect any warnings or errors.", result.getErrors().size() == 0 && result.getWarnings().size() == 0);
     }
 
@@ -55,7 +58,7 @@
     }
 
     private void testMissingExplicitContext(String script) {
-        CompilationResult result = compile(script);
+        CompilationResult result = compileFile(script);
         List<CompilerMessage> warnings = result.getWarnings();
         assertTrue(script + ": Expected compilation warnings.", warnings.size() == 1);
         CompilerMessage warningMessage = warnings.get(0);
@@ -91,7 +94,7 @@
     @Test
     public void testSensitiveAttributes() {
         String script = "/sensitive-attributes.html";
-        CompilationResult result = compile(script);
+        CompilationResult result = compileFile(script);
         List<CompilerMessage> warnings = result.getWarnings();
         assertTrue("Expected compilation warnings.", warnings.size() == 2);
         CompilerMessage _1stWarning = warnings.get(0);
@@ -110,15 +113,72 @@
     @Test
     public void testErrorReporting1() {
         String script = "/error-1.html";
-        CompilationResult result = compile(script);
+        CompilationResult result = compileFile(script);
         List<CompilerMessage> errors = result.getErrors();
         assertTrue("Expected compilation errors.", errors.size() == 1);
         CompilerMessage error = errors.get(0);
         assertEquals("Error is not reported at the expected line.", 18, error.getLine());
     }
 
-    private CompilationResult compile(String file) {
-        CompilationUnit compilationUnit = TestUtils.readScriptFromClasspath(file);
+    @Test
+    public void testNumberParsing() {
+        // integers
+        int integerTestRange = 20;
+        for (int i = -1 * integerTestRange; i < integerTestRange; i++) {
+            assertEquals(0, compileSource("${" + i + "}").getErrors().size());
+        }
+
+        // doubles
+        double  doubleTestRange = 20.00;
+        for (double i = -1.00 * doubleTestRange; i < doubleTestRange; i+= 0.1) {
+            assertEquals(0, compileSource("${" + i + "}").getErrors().size());
+        }
+
+        assertEquals(0, compileSource("${-0.0}").getErrors().size());
+        assertEquals(0, compileSource("${-0.000}").getErrors().size());
+        assertEquals(1, compileSource("${-00.0}").getErrors().size());
+        assertEquals(1, compileSource("${00.0}").getErrors().size());
+        assertEquals(1, compileSource("${00}").getErrors().size());
+        assertEquals(1, compileSource("${-0}").getErrors().size());
+        assertEquals(1, compileSource("${01}").getErrors().size());
+        assertEquals(0, compileSource("${0.1e-2}").getErrors().size());
+        assertEquals(0, compileSource("${0.1e+2}").getErrors().size());
+        assertEquals(1, compileSource("${00.1e-2}").getErrors().size());
+        assertEquals(1, compileSource("${0e-2}").getErrors().size());
+        assertEquals(1, compileSource("${01e-2}").getErrors().size());
+        assertEquals(0, compileSource("${1e-2}").getErrors().size());
+        assertEquals(0, compileSource("${1e+2}").getErrors().size());
+    }
+
+    private CompilationResult compileFile(final String file) {
+        InputStream stream = this.getClass().getResourceAsStream(file);
+        final Reader reader = new InputStreamReader(stream);
+        CompilationUnit compilationUnit = new CompilationUnit() {
+            @Override
+            public String getScriptName() {
+                return file;
+            }
+
+            @Override
+            public Reader getScriptReader() {
+                return reader;
+            }
+        };
+        return compiler.compile(compilationUnit);
+    }
+
+    private CompilationResult compileSource(final String source) {
+        CompilationUnit compilationUnit = new CompilationUnit() {
+            @Override
+            public String getScriptName() {
+                return "NO_NAME";
+            }
+
+            @Override
+            public Reader getScriptReader() {
+                return new StringReader(source);
+            }
+        };
         return compiler.compile(compilationUnit);
     }