http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.f3ac
new file mode 100644
index 0000000..738dc76
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.f3ac
@@ -0,0 +1,350 @@
+<#--
+  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.
+-->
+<#-- A version of "?join" that fails at null-s in the sequence: -->
+<#function join(seq, sep='')>
+  <#local r = "">
+  <#list seq as i>
+    <#local r = r + i>
+    <#if i_has_next>
+      <#local r = r + sep>
+    </#if>
+  </#list>
+  <#return r>
+</#function>
+
+<#----------------------->
+<#-- Range expressions -->
+
+<@assertEquals actual=join(1..2, ' ') expected="1 2" />
+<@assertEquals actual=join(1..1, ' ') expected="1" />
+<@assertEquals actual=join(1..0, ' ') expected="1 0" />
+<@assertEquals actual=join(1..-1, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..-1, ' ') expected="-1" />
+<@assertEquals actual=join(-1..1, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=join(1..<3, ' ') expected="1 2" />
+<@assertEquals actual=join(1..<2, ' ') expected="1" />
+<@assertEquals actual=join(1..<1, ' ') expected="" />
+<@assertEquals actual=join(1..<0, ' ') expected="1" />
+<@assertEquals actual=join(1..<-1, ' ') expected="1 0" />
+<@assertEquals actual=join(1..<-2, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..<0, ' ') expected="-1" />
+<@assertEquals actual=join(-1..<2, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=join(1..!3, ' ') expected="1 2" />
+<@assertEquals actual=join(1..!2, ' ') expected="1" />
+<@assertEquals actual=join(1..!1, ' ') expected="" />
+<@assertEquals actual=join(1..!0, ' ') expected="1" />
+<@assertEquals actual=join(1..!-1, ' ') expected="1 0" />
+<@assertEquals actual=join(1..!-2, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..!0, ' ') expected="-1" />
+<@assertEquals actual=join(-1..!2, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=join(1..*2, ' ') expected="1 2" />
+<@assertEquals actual=join(1..*1, ' ') expected="1" />
+<@assertEquals actual=join(1..*0, ' ') expected="" />
+<@assertEquals actual=join(1..*-1, ' ') expected="1" />
+<@assertEquals actual=join(1..*-2, ' ') expected="1 0" />
+<@assertEquals actual=join(1..*-3, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..*1, ' ') expected="-1" />
+<@assertEquals actual=join(-1..*3, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=1 expected=(0..0)?size />
+<@assertEquals actual=1 expected=(1..1)?size />
+<@assertEquals actual=1 expected=(2..2)?size />
+<@assertEquals actual=2 expected=(0..1)?size />
+<@assertEquals actual=2 expected=(1..2)?size />
+<@assertEquals actual=2 expected=(2..3)?size />
+<@assertEquals actual=3 expected=(2..4)?size />
+<@assertEquals actual=2 expected=(1..0)?size />
+<@assertEquals actual=2 expected=(2..1)?size />
+<@assertEquals actual=2 expected=(3..2)?size />
+<@assertEquals actual=3 expected=(4..2)?size />
+
+<@assertEquals actual=0 expected=(0..<0)?size />
+<@assertEquals actual=0 expected=(1..<1)?size />
+<@assertEquals actual=0 expected=(2..<2)?size />
+<@assertEquals actual=1 expected=(0..<1)?size />
+<@assertEquals actual=1 expected=(1..<2)?size />
+<@assertEquals actual=1 expected=(2..<3)?size />
+<@assertEquals actual=2 expected=(2..<4)?size />
+<@assertEquals actual=1 expected=(1..<0)?size />
+<@assertEquals actual=1 expected=(2..<1)?size />
+<@assertEquals actual=1 expected=(3..<2)?size />
+<@assertEquals actual=2 expected=(4..<2)?size />
+
+<@assertEquals actual=0 expected=(0..*0)?size />
+<@assertEquals actual=0 expected=(1..*0)?size />
+<@assertEquals actual=0 expected=(2..*0)?size />
+<@assertEquals actual=1 expected=(0..*1)?size />
+<@assertEquals actual=1 expected=(1..*1)?size />
+<@assertEquals actual=1 expected=(2..*1)?size />
+<@assertEquals actual=2 expected=(2..*2)?size />
+<@assertEquals actual=1 expected=(0..*-1)?size />
+<@assertEquals actual=1 expected=(1..*-1)?size />
+<@assertEquals actual=1 expected=(2..*-1)?size />
+<@assertEquals actual=2 expected=(0..*-2)?size />
+<@assertEquals actual=2 expected=(1..*-2)?size />
+<@assertEquals actual=2 expected=(2..*-2)?size />
+
+
+<#--------------------->
+<#-- String slicing: -->
+
+<#assign s = 'abcd'>
+
+<@assertEquals actual=s[0..] expected="abcd" />
+<@assertEquals actual=s[1..] expected="bcd" />
+<@assertEquals actual=s[2..] expected="cd" />
+<@assertEquals actual=s[3..] expected="d" />
+<@assertEquals actual=s[4..] expected="" />
+<@assertFails message="5 is out of bounds">
+  <#assign _ = s[5..] />
+</@assertFails>
+<@assertFails message="6 is out of bounds">
+  <#assign _ = s[6..] />
+</@assertFails>
+
+<@assertEquals actual=s[1..2] expected="bc" />
+<@assertEquals actual=s[1..1] expected="b" />
+<@assertEquals actual=s[0..1] expected="ab" />
+<@assertEquals actual=s[0..0] expected="a" />
+<@assertFails message="4 is out of bounds">
+  <#assign _ = s[1..4] />
+</@assertFails>
+<@assertFails message="5 is out of bounds">
+  <#assign _ = s[1..5] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-1..1] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-2..1] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[0..-1] />
+</@assertFails>
+
+<@assertEquals actual=s[1..<3] expected="bc" />
+<@assertEquals actual=s[1..!3] expected="bc" />
+<@assertEquals actual=s[1..<2] expected="b" />
+<@assertEquals actual=s[1..<0] expected="b" />
+<@assertEquals actual=s[1..<1] expected="" />
+<@assertEquals actual=s[0..<0] expected="" />
+<@assertEquals actual=s[5..<5] expected="" />
+<@assertEquals actual=s[6..<6] expected="" />
+<@assertEquals actual=s[-5..<-5] expected="" />
+<@assertFails message="negative">
+  <#assign _ = s[-5..<1] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[2..<-4] />
+</@assertFails>
+<@assertFails message="decreasing">
+  <#assign _ = s[2..<0] />
+</@assertFails>
+
+<@assertEquals actual=s[1..*-1] expected="b" />
+<@assertEquals actual=s[1..*0] expected="" />
+<@assertEquals actual=s[1..*1] expected="b" />
+<@assertEquals actual=s[1..*2] expected="bc" />
+<@assertEquals actual=s[1..*3] expected="bcd" />
+<@assertEquals actual=s[1..*4] expected="bcd" />
+<@assertEquals actual=s[1..*5] expected="bcd" />
+<@assertEquals actual=s[4..*1] expected="" />
+<@assertEquals actual=s[5..*0] expected="" />
+<@assertEquals actual=s[6..*0] expected="" />
+<@assertEquals actual=s[-5..*0] expected="" />
+<@assertEquals actual=s[0..*0] expected="" />
+<@assertEquals actual=s[0..*-1] expected="a" />
+<@assertEquals actual=s[0..*-2] expected="a" />
+<@assertEquals actual=s[0..*-3] expected="a" />
+<@assertFails message="5 is out of bounds">
+  <#assign _ = s[5..*1] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-1..*1] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-2..*1] />
+</@assertFails>
+<@assertFails message="decreasing">
+  <#assign _ = s[1..*-2] />
+</@assertFails>
+<@assertFails message="decreasing">
+  <#assign _ = s[1..*-3] />
+</@assertFails>
+<@assertFails message="4 is out of bounds">
+  <#assign _ = s[4..*-1] />
+</@assertFails>
+
+<#-- FreeMarker 2 string backward-range bug not supported anymore: -->
+<@assertFails message="decreasing">
+  <#assign _ = s[1..0] />
+</@assertFails>
+<@assertFails message="decreasing">
+  <#assign _ = s[2..1] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[0..-1] />
+</@assertFails>
+<@assertFails message="decreasing">
+  <#assign _ = s[3..1] />
+</@assertFails>
+<#-- The bug was never emulated for operators introduced after 2.3.20: -->
+<@assertFails message="decreasing">
+  <#assign _ = s[3..<1] />
+</@assertFails>
+<@assertFails message="decreasing">
+  <#assign _ = s[3..*-2] />
+</@assertFails>
+
+<#assign r = 1..2>
+<@assertEquals actual=s[r] expected="bc" />
+<#assign r = 2..1>
+<@assertFails message="decreasing">
+  <#assign _ = s[r] />
+</@assertFails>
+<#assign r = 1..<2>
+<@assertEquals actual=s[r] expected="b" />
+<#assign r = 2..<4>
+<@assertEquals actual=s[r] expected="cd" />
+<#assign r = 2..>
+<@assertEquals actual=s[r] expected="cd" />
+<#assign r = 1..*2>
+<@assertEquals actual=s[r] expected="bc" />
+
+<#----------------------->
+<#-- Sequence slicing: -->
+
+<#assign s = ['a', 'b', 'c', 'd']>
+
+<@assertEquals actual=join(s[0..]) expected="abcd" />
+<@assertEquals actual=join(s[1..]) expected="bcd" />
+<@assertEquals actual=join(s[2..]) expected="cd" />
+<@assertEquals actual=join(s[3..]) expected="d" />
+<@assertEquals actual=join(s[4..]) expected="" />
+<@assertFails message="5 is out of bounds">
+  <#assign _ = s[5..] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-1..] />
+</@assertFails>
+
+<@assertEquals actual=join(s[1..2]) expected="bc" />
+<@assertEquals actual=join(s[1..1]) expected="b" />
+<@assertEquals actual=join(s[0..1]) expected="ab" />
+<@assertEquals actual=join(s[0..0]) expected="a" />
+<@assertFails message="5 is out of bounds">
+  <#assign _ = s[1..5] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-1..0] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[0..-1] />
+</@assertFails>
+
+<@assertEquals actual=join(s[1..<3]) expected="bc" />
+<@assertEquals actual=join(s[1..!3]) expected="bc" />
+<@assertEquals actual=join(s[1..<2]) expected="b" />
+<@assertEquals actual=join(s[1..<0]) expected="b" />
+<@assertEquals actual=join(s[1..<1]) expected="" />
+<@assertEquals actual=join(s[0..<0]) expected="" />
+
+<@assertEquals actual=join(s[1..0]) expected="ba" />
+<@assertEquals actual=join(s[2..1]) expected="cb" />
+<@assertEquals actual=join(s[2..0]) expected="cba" />
+<@assertEquals actual=join(s[2..<0]) expected="cb" />
+<@assertEquals actual=join(s[1..<0]) expected="b" />
+<@assertEquals actual=join(s[0..<0]) expected="" />
+<@assertEquals actual=join(s[3..<1]) expected="dc" />
+<@assertEquals actual=join(s[2..<1]) expected="c" />
+<@assertEquals actual=join(s[1..<1]) expected="" />
+<@assertEquals actual=join(s[0..<1]) expected="a" />
+<@assertEquals actual=join(s[0..<0]) expected="" />
+<@assertEquals actual=join(s[5..<5]) expected="" />
+<@assertEquals actual=join(s[-5..<-5]) expected="" />
+
+<@assertEquals actual=join(s[0..*-4]) expected="a" />
+<@assertEquals actual=join(s[1..*-4]) expected="ba" />
+<@assertEquals actual=join(s[1..*-3]) expected="ba" />
+<@assertEquals actual=join(s[1..*-2]) expected="ba" />
+<@assertEquals actual=join(s[1..*-1]) expected="b" />
+<@assertEquals actual=join(s[1..*0]) expected="" />
+<@assertEquals actual=join(s[1..*1]) expected="b" />
+<@assertEquals actual=join(s[1..*2]) expected="bc" />
+<@assertEquals actual=join(s[1..*3]) expected="bcd" />
+<@assertEquals actual=join(s[1..*4]) expected="bcd" />
+<@assertEquals actual=join(s[1..*5]) expected="bcd" />
+<@assertEquals actual=join(s[0..*3]) expected="abc" />
+<@assertEquals actual=join(s[2..*3]) expected="cd" />
+<@assertEquals actual=join(s[3..*3]) expected="d" />
+<@assertEquals actual=join(s[4..*3]) expected="" />
+<@assertFails message="5 is out of bounds">
+  <#assign _ = s[5..*3] />
+</@assertFails>
+<@assertFails message="negative">
+  <#assign _ = s[-1..*2] />
+</@assertFails>
+
+<#assign r = 1..2>
+<@assertEquals actual=join(s[r]) expected="bc" />
+<#assign r = 2..0>
+<@assertEquals actual=join(s[r]) expected="cba" />
+<#assign r = 1..<2>
+<@assertEquals actual=join(s[r]) expected="b" />
+<#assign r = 2..<0>
+<@assertEquals actual=join(s[r]) expected="cb" />
+<#assign r = 2..>
+<@assertEquals actual=join(s[r]) expected="cd" />
+<#assign r = 1..*2>
+<@assertEquals actual=join(s[r]) expected="bc" />
+<#assign r = 1..*-9>
+<@assertEquals actual=join(s[r]) expected="ba" />
+
+<@assertEquals actual=(4..)?size expected=2147483647 />
+<@assertEquals actual=limitedJoin(4.., 3) expected="4, 5, 6, ..." />
+
+<@assertEquals actual=(4..)[0] expected=4 />
+<@assertEquals actual=(4..)[1] expected=5 />
+<@assertEquals actual=(4..)[1000000] expected=1000004 />
+<@assert !(4..)[-1]?? />
+<@assert !(1..2)[2]?? />
+
+<#assign r = 2147483646..>
+<@assertEquals actual=r?size expected=2147483647 />
+<@assertEquals actual=limitedJoin(r, 3) expected="2147483646, 2147483647, 
2147483648, ..." />
+<@assertEquals actual=r[100] expected=2147483746 />
+
+<#assign r = -2..>
+<@assertEquals actual=limitedJoin(r, 5) expected="-2, -1, 0, 1, 2, ..." />
+<@assertEquals actual=r[0] expected=-2 />
+<@assertEquals actual=r[1] expected=-1 />
+
+<#function limitedJoin(range, limit)>
+       <#assign joined="">
+       <#list range as i>
+               <#assign joined = joined + i?c>
+               <#if i_has_next><#assign joined = joined + ', '></#if>
+               <#local limit = limit - 1>
+               <#if limit == 0><#assign joined = joined + "..."><#break></#if>
+       </#list>
+       <#return joined>
+</#function>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
deleted file mode 100644
index 738dc76..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
+++ /dev/null
@@ -1,350 +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.
--->
-<#-- A version of "?join" that fails at null-s in the sequence: -->
-<#function join(seq, sep='')>
-  <#local r = "">
-  <#list seq as i>
-    <#local r = r + i>
-    <#if i_has_next>
-      <#local r = r + sep>
-    </#if>
-  </#list>
-  <#return r>
-</#function>
-
-<#----------------------->
-<#-- Range expressions -->
-
-<@assertEquals actual=join(1..2, ' ') expected="1 2" />
-<@assertEquals actual=join(1..1, ' ') expected="1" />
-<@assertEquals actual=join(1..0, ' ') expected="1 0" />
-<@assertEquals actual=join(1..-1, ' ') expected="1 0 -1" />
-<@assertEquals actual=join(-1..-1, ' ') expected="-1" />
-<@assertEquals actual=join(-1..1, ' ') expected="-1 0 1" />
-
-<@assertEquals actual=join(1..<3, ' ') expected="1 2" />
-<@assertEquals actual=join(1..<2, ' ') expected="1" />
-<@assertEquals actual=join(1..<1, ' ') expected="" />
-<@assertEquals actual=join(1..<0, ' ') expected="1" />
-<@assertEquals actual=join(1..<-1, ' ') expected="1 0" />
-<@assertEquals actual=join(1..<-2, ' ') expected="1 0 -1" />
-<@assertEquals actual=join(-1..<0, ' ') expected="-1" />
-<@assertEquals actual=join(-1..<2, ' ') expected="-1 0 1" />
-
-<@assertEquals actual=join(1..!3, ' ') expected="1 2" />
-<@assertEquals actual=join(1..!2, ' ') expected="1" />
-<@assertEquals actual=join(1..!1, ' ') expected="" />
-<@assertEquals actual=join(1..!0, ' ') expected="1" />
-<@assertEquals actual=join(1..!-1, ' ') expected="1 0" />
-<@assertEquals actual=join(1..!-2, ' ') expected="1 0 -1" />
-<@assertEquals actual=join(-1..!0, ' ') expected="-1" />
-<@assertEquals actual=join(-1..!2, ' ') expected="-1 0 1" />
-
-<@assertEquals actual=join(1..*2, ' ') expected="1 2" />
-<@assertEquals actual=join(1..*1, ' ') expected="1" />
-<@assertEquals actual=join(1..*0, ' ') expected="" />
-<@assertEquals actual=join(1..*-1, ' ') expected="1" />
-<@assertEquals actual=join(1..*-2, ' ') expected="1 0" />
-<@assertEquals actual=join(1..*-3, ' ') expected="1 0 -1" />
-<@assertEquals actual=join(-1..*1, ' ') expected="-1" />
-<@assertEquals actual=join(-1..*3, ' ') expected="-1 0 1" />
-
-<@assertEquals actual=1 expected=(0..0)?size />
-<@assertEquals actual=1 expected=(1..1)?size />
-<@assertEquals actual=1 expected=(2..2)?size />
-<@assertEquals actual=2 expected=(0..1)?size />
-<@assertEquals actual=2 expected=(1..2)?size />
-<@assertEquals actual=2 expected=(2..3)?size />
-<@assertEquals actual=3 expected=(2..4)?size />
-<@assertEquals actual=2 expected=(1..0)?size />
-<@assertEquals actual=2 expected=(2..1)?size />
-<@assertEquals actual=2 expected=(3..2)?size />
-<@assertEquals actual=3 expected=(4..2)?size />
-
-<@assertEquals actual=0 expected=(0..<0)?size />
-<@assertEquals actual=0 expected=(1..<1)?size />
-<@assertEquals actual=0 expected=(2..<2)?size />
-<@assertEquals actual=1 expected=(0..<1)?size />
-<@assertEquals actual=1 expected=(1..<2)?size />
-<@assertEquals actual=1 expected=(2..<3)?size />
-<@assertEquals actual=2 expected=(2..<4)?size />
-<@assertEquals actual=1 expected=(1..<0)?size />
-<@assertEquals actual=1 expected=(2..<1)?size />
-<@assertEquals actual=1 expected=(3..<2)?size />
-<@assertEquals actual=2 expected=(4..<2)?size />
-
-<@assertEquals actual=0 expected=(0..*0)?size />
-<@assertEquals actual=0 expected=(1..*0)?size />
-<@assertEquals actual=0 expected=(2..*0)?size />
-<@assertEquals actual=1 expected=(0..*1)?size />
-<@assertEquals actual=1 expected=(1..*1)?size />
-<@assertEquals actual=1 expected=(2..*1)?size />
-<@assertEquals actual=2 expected=(2..*2)?size />
-<@assertEquals actual=1 expected=(0..*-1)?size />
-<@assertEquals actual=1 expected=(1..*-1)?size />
-<@assertEquals actual=1 expected=(2..*-1)?size />
-<@assertEquals actual=2 expected=(0..*-2)?size />
-<@assertEquals actual=2 expected=(1..*-2)?size />
-<@assertEquals actual=2 expected=(2..*-2)?size />
-
-
-<#--------------------->
-<#-- String slicing: -->
-
-<#assign s = 'abcd'>
-
-<@assertEquals actual=s[0..] expected="abcd" />
-<@assertEquals actual=s[1..] expected="bcd" />
-<@assertEquals actual=s[2..] expected="cd" />
-<@assertEquals actual=s[3..] expected="d" />
-<@assertEquals actual=s[4..] expected="" />
-<@assertFails message="5 is out of bounds">
-  <#assign _ = s[5..] />
-</@assertFails>
-<@assertFails message="6 is out of bounds">
-  <#assign _ = s[6..] />
-</@assertFails>
-
-<@assertEquals actual=s[1..2] expected="bc" />
-<@assertEquals actual=s[1..1] expected="b" />
-<@assertEquals actual=s[0..1] expected="ab" />
-<@assertEquals actual=s[0..0] expected="a" />
-<@assertFails message="4 is out of bounds">
-  <#assign _ = s[1..4] />
-</@assertFails>
-<@assertFails message="5 is out of bounds">
-  <#assign _ = s[1..5] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-1..1] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-2..1] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[0..-1] />
-</@assertFails>
-
-<@assertEquals actual=s[1..<3] expected="bc" />
-<@assertEquals actual=s[1..!3] expected="bc" />
-<@assertEquals actual=s[1..<2] expected="b" />
-<@assertEquals actual=s[1..<0] expected="b" />
-<@assertEquals actual=s[1..<1] expected="" />
-<@assertEquals actual=s[0..<0] expected="" />
-<@assertEquals actual=s[5..<5] expected="" />
-<@assertEquals actual=s[6..<6] expected="" />
-<@assertEquals actual=s[-5..<-5] expected="" />
-<@assertFails message="negative">
-  <#assign _ = s[-5..<1] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[2..<-4] />
-</@assertFails>
-<@assertFails message="decreasing">
-  <#assign _ = s[2..<0] />
-</@assertFails>
-
-<@assertEquals actual=s[1..*-1] expected="b" />
-<@assertEquals actual=s[1..*0] expected="" />
-<@assertEquals actual=s[1..*1] expected="b" />
-<@assertEquals actual=s[1..*2] expected="bc" />
-<@assertEquals actual=s[1..*3] expected="bcd" />
-<@assertEquals actual=s[1..*4] expected="bcd" />
-<@assertEquals actual=s[1..*5] expected="bcd" />
-<@assertEquals actual=s[4..*1] expected="" />
-<@assertEquals actual=s[5..*0] expected="" />
-<@assertEquals actual=s[6..*0] expected="" />
-<@assertEquals actual=s[-5..*0] expected="" />
-<@assertEquals actual=s[0..*0] expected="" />
-<@assertEquals actual=s[0..*-1] expected="a" />
-<@assertEquals actual=s[0..*-2] expected="a" />
-<@assertEquals actual=s[0..*-3] expected="a" />
-<@assertFails message="5 is out of bounds">
-  <#assign _ = s[5..*1] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-1..*1] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-2..*1] />
-</@assertFails>
-<@assertFails message="decreasing">
-  <#assign _ = s[1..*-2] />
-</@assertFails>
-<@assertFails message="decreasing">
-  <#assign _ = s[1..*-3] />
-</@assertFails>
-<@assertFails message="4 is out of bounds">
-  <#assign _ = s[4..*-1] />
-</@assertFails>
-
-<#-- FreeMarker 2 string backward-range bug not supported anymore: -->
-<@assertFails message="decreasing">
-  <#assign _ = s[1..0] />
-</@assertFails>
-<@assertFails message="decreasing">
-  <#assign _ = s[2..1] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[0..-1] />
-</@assertFails>
-<@assertFails message="decreasing">
-  <#assign _ = s[3..1] />
-</@assertFails>
-<#-- The bug was never emulated for operators introduced after 2.3.20: -->
-<@assertFails message="decreasing">
-  <#assign _ = s[3..<1] />
-</@assertFails>
-<@assertFails message="decreasing">
-  <#assign _ = s[3..*-2] />
-</@assertFails>
-
-<#assign r = 1..2>
-<@assertEquals actual=s[r] expected="bc" />
-<#assign r = 2..1>
-<@assertFails message="decreasing">
-  <#assign _ = s[r] />
-</@assertFails>
-<#assign r = 1..<2>
-<@assertEquals actual=s[r] expected="b" />
-<#assign r = 2..<4>
-<@assertEquals actual=s[r] expected="cd" />
-<#assign r = 2..>
-<@assertEquals actual=s[r] expected="cd" />
-<#assign r = 1..*2>
-<@assertEquals actual=s[r] expected="bc" />
-
-<#----------------------->
-<#-- Sequence slicing: -->
-
-<#assign s = ['a', 'b', 'c', 'd']>
-
-<@assertEquals actual=join(s[0..]) expected="abcd" />
-<@assertEquals actual=join(s[1..]) expected="bcd" />
-<@assertEquals actual=join(s[2..]) expected="cd" />
-<@assertEquals actual=join(s[3..]) expected="d" />
-<@assertEquals actual=join(s[4..]) expected="" />
-<@assertFails message="5 is out of bounds">
-  <#assign _ = s[5..] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-1..] />
-</@assertFails>
-
-<@assertEquals actual=join(s[1..2]) expected="bc" />
-<@assertEquals actual=join(s[1..1]) expected="b" />
-<@assertEquals actual=join(s[0..1]) expected="ab" />
-<@assertEquals actual=join(s[0..0]) expected="a" />
-<@assertFails message="5 is out of bounds">
-  <#assign _ = s[1..5] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-1..0] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[0..-1] />
-</@assertFails>
-
-<@assertEquals actual=join(s[1..<3]) expected="bc" />
-<@assertEquals actual=join(s[1..!3]) expected="bc" />
-<@assertEquals actual=join(s[1..<2]) expected="b" />
-<@assertEquals actual=join(s[1..<0]) expected="b" />
-<@assertEquals actual=join(s[1..<1]) expected="" />
-<@assertEquals actual=join(s[0..<0]) expected="" />
-
-<@assertEquals actual=join(s[1..0]) expected="ba" />
-<@assertEquals actual=join(s[2..1]) expected="cb" />
-<@assertEquals actual=join(s[2..0]) expected="cba" />
-<@assertEquals actual=join(s[2..<0]) expected="cb" />
-<@assertEquals actual=join(s[1..<0]) expected="b" />
-<@assertEquals actual=join(s[0..<0]) expected="" />
-<@assertEquals actual=join(s[3..<1]) expected="dc" />
-<@assertEquals actual=join(s[2..<1]) expected="c" />
-<@assertEquals actual=join(s[1..<1]) expected="" />
-<@assertEquals actual=join(s[0..<1]) expected="a" />
-<@assertEquals actual=join(s[0..<0]) expected="" />
-<@assertEquals actual=join(s[5..<5]) expected="" />
-<@assertEquals actual=join(s[-5..<-5]) expected="" />
-
-<@assertEquals actual=join(s[0..*-4]) expected="a" />
-<@assertEquals actual=join(s[1..*-4]) expected="ba" />
-<@assertEquals actual=join(s[1..*-3]) expected="ba" />
-<@assertEquals actual=join(s[1..*-2]) expected="ba" />
-<@assertEquals actual=join(s[1..*-1]) expected="b" />
-<@assertEquals actual=join(s[1..*0]) expected="" />
-<@assertEquals actual=join(s[1..*1]) expected="b" />
-<@assertEquals actual=join(s[1..*2]) expected="bc" />
-<@assertEquals actual=join(s[1..*3]) expected="bcd" />
-<@assertEquals actual=join(s[1..*4]) expected="bcd" />
-<@assertEquals actual=join(s[1..*5]) expected="bcd" />
-<@assertEquals actual=join(s[0..*3]) expected="abc" />
-<@assertEquals actual=join(s[2..*3]) expected="cd" />
-<@assertEquals actual=join(s[3..*3]) expected="d" />
-<@assertEquals actual=join(s[4..*3]) expected="" />
-<@assertFails message="5 is out of bounds">
-  <#assign _ = s[5..*3] />
-</@assertFails>
-<@assertFails message="negative">
-  <#assign _ = s[-1..*2] />
-</@assertFails>
-
-<#assign r = 1..2>
-<@assertEquals actual=join(s[r]) expected="bc" />
-<#assign r = 2..0>
-<@assertEquals actual=join(s[r]) expected="cba" />
-<#assign r = 1..<2>
-<@assertEquals actual=join(s[r]) expected="b" />
-<#assign r = 2..<0>
-<@assertEquals actual=join(s[r]) expected="cb" />
-<#assign r = 2..>
-<@assertEquals actual=join(s[r]) expected="cd" />
-<#assign r = 1..*2>
-<@assertEquals actual=join(s[r]) expected="bc" />
-<#assign r = 1..*-9>
-<@assertEquals actual=join(s[r]) expected="ba" />
-
-<@assertEquals actual=(4..)?size expected=2147483647 />
-<@assertEquals actual=limitedJoin(4.., 3) expected="4, 5, 6, ..." />
-
-<@assertEquals actual=(4..)[0] expected=4 />
-<@assertEquals actual=(4..)[1] expected=5 />
-<@assertEquals actual=(4..)[1000000] expected=1000004 />
-<@assert !(4..)[-1]?? />
-<@assert !(1..2)[2]?? />
-
-<#assign r = 2147483646..>
-<@assertEquals actual=r?size expected=2147483647 />
-<@assertEquals actual=limitedJoin(r, 3) expected="2147483646, 2147483647, 
2147483648, ..." />
-<@assertEquals actual=r[100] expected=2147483746 />
-
-<#assign r = -2..>
-<@assertEquals actual=limitedJoin(r, 5) expected="-2, -1, 0, 1, 2, ..." />
-<@assertEquals actual=r[0] expected=-2 />
-<@assertEquals actual=r[1] expected=-1 />
-
-<#function limitedJoin(range, limit)>
-       <#assign joined="">
-       <#list range as i>
-               <#assign joined = joined + i?c>
-               <#if i_has_next><#assign joined = joined + ', '></#if>
-               <#local limit = limit - 1>
-               <#if limit == 0><#assign joined = joined + "..."><#break></#if>
-       </#list>
-       <#return joined>
-</#function>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.f3ac
new file mode 100644
index 0000000..deb0d13
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.f3ac
@@ -0,0 +1,47 @@
+<#--
+  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.
+-->
+<#attempt>
+ <#assign sequence = ["Hello, World"]>
+ ${sequence[0]}
+<#recover>
+  We should never get here.
+</#attempt>
+<#attempt>
+ Let's try to output an undefined variable: ${undefinedVariable}
+<#recover>
+ Well, that did not work.<@assert .error?contains('undefinedVariable') />
+ Now we nest another attempt/recover here:
+ <#attempt>
+   ${sequence[1]}
+ <#recover>
+   Oops...<@assert .error?contains('sequence[1]') />
+   Remember, freeMarker sequences are zero-based! ${sequence[0]}
+ </#attempt>
+ Now we check the current error message.<@assert 
.error?contains('undefinedVariable') />
+</#attempt>
+<#attempt>
+  <#include "nonexistent_template">
+<#recover>
+  The template is not currently available
+</#attempt>
+<#attempt>
+  <#include "undefined.f3ac">
+<#recover>
+  The included template had a problem.<@assert 
.error?contains('undefined_variable') />
+</#attempt>

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
deleted file mode 100644
index 3fd916b..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
+++ /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.
--->
-<#attempt>
- <#assign sequence = ["Hello, World"]>
- ${sequence[0]}
-<#recover>
-  We should never get here.
-</#attempt>
-<#attempt>
- Let's try to output an undefined variable: ${undefinedVariable}
-<#recover>
- Well, that did not work.<@assert .error?contains('undefinedVariable') />
- Now we nest another attempt/recover here:
- <#attempt>
-   ${sequence[1]}
- <#recover>
-   Oops...<@assert .error?contains('sequence[1]') />
-   Remember, freeMarker sequences are zero-based! ${sequence[0]}
- </#attempt>
- Now we check the current error message.<@assert 
.error?contains('undefinedVariable') />
-</#attempt>
-<#attempt>
-  <#include "nonexistent_template">
-<#recover>
-  The template is not currently available
-</#attempt>
-<#attempt>
-  <#include "undefined.ftl">
-<#recover>
-  The included template had a problem.<@assert 
.error?contains('undefined_variable') />
-</#attempt>

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.f3ac
new file mode 100644
index 0000000..e6b19a0
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.f3ac
@@ -0,0 +1,47 @@
+<#--
+  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.
+-->
+<html>
+<head>
+<title>FreeMarker: Root Lookup Test</title>
+</head>
+<body>
+
+<p>A simple test follows:</p>
+
+<p>${message}</p>
+
+<p>Access the same variable via the root variable (dot syntax):</p>
+
+<p>${.dataModel.message}</p>
+
+<p>Access the same variable via the root variable (bracket syntax):</p>
+
+<p>${.dataModel["message"]}</p>
+
+<p>Ensure that root lookups are unaffected by local variables:</p>
+
+<#macro test message{positional}>
+  ${.dataModel.message}
+  ${message}
+</#macro>
+
+<@test message + " Part Deux" />
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
deleted file mode 100644
index e6b19a0..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
+++ /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.
--->
-<html>
-<head>
-<title>FreeMarker: Root Lookup Test</title>
-</head>
-<body>
-
-<p>A simple test follows:</p>
-
-<p>${message}</p>
-
-<p>Access the same variable via the root variable (dot syntax):</p>
-
-<p>${.dataModel.message}</p>
-
-<p>Access the same variable via the root variable (bracket syntax):</p>
-
-<p>${.dataModel["message"]}</p>
-
-<p>Ensure that root lookups are unaffected by local variables:</p>
-
-<#macro test message{positional}>
-  ${.dataModel.message}
-  ${message}
-</#macro>
-
-<@test message + " Part Deux" />
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
new file mode 100644
index 0000000..b8cb070
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
@@ -0,0 +1,360 @@
+<#--
+  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.
+-->
+<@noOutput>
+<#setting locale="en_US">
+<#setting numberFormat="0.#########">
+
+<#assign ls = []?sort>
+<#list ls as i>
+- ${i}
+</#list>
+<@assertEquals expected=0 actual=ls?size />
+<@assertEquals expected=3 actual=set?size />
+</@noOutput>
+Sorting strings:
+----------------
+
+String order:
+<#assign ls = ["whale", "Barbara", "zeppelin", "aardvark", "beetroot"]?sort>
+<#list ls as i>
+- ${i}
+</#list>
+
+First: ${ls?first}
+Last: ${ls?last}
+Size ${ls?size}
+
+Numerical order:
+<#assign ls = [123?byte, 543, -324, -34?float, 0.11, 0, 111?int, 0.1?double, 
1, 5]?sort>
+<#list ls as i>
+- ${i}
+</#list>
+
+First: ${ls?first}
+Last: ${ls?last}
+Size ${ls?size}
+
+Date/time order:
+<#assign x = [
+        '08:05'?time('HH:mm'),
+        '18:00'?time('HH:mm'),
+        '06:05'?time('HH:mm'),
+        '08:15'?time('HH:mm')]>
+<#list x?sort as i>
+- ${i?string('HH:mm')}
+</#list>
+
+Boolean order:
+<#assign x = [
+        true,
+        false,
+        false,
+        true]>
+<#list x?sort as i>
+- ${i?string}
+</#list>
+
+
+Sorting hashes:
+---------------
+
+<#assign ls = [
+  {"name":"whale", "weight":2000?short},
+  {"name":"Barbara", "weight":53},
+  {"name":"zeppelin", "weight":-200?float},
+  {"name":"aardvark", "weight":30?long},
+  {"name":"beetroot", "weight":0.3}
+]>
+Order by name:
+<#assign ls = ls?sortBy("name")>
+<#list ls as i>
+- ${i.name}: ${i.weight}
+</#list>
+
+Order by weight:
+<#assign ls = ls?sortBy("weight")>
+<#list ls as i>
+- ${i.name}: ${i.weight}
+</#list>
+
+Order by a.x.v:
+<#assign x = [
+        {"a": {"x": {"v": "qweqw", "w": "asd"}, "y": 
'1998-02-20'?date('yyyy-MM-dd')}},
+        {"a": {"x": {"v": "aqweqw", "w": "asd"}, "y": 
'1999-01-20'?date('yyyy-MM-dd')}},
+        {"a": {"x": {"v": "dfgdf", "w": "asd"}, "y": 
'1999-04-20'?date('yyyy-MM-dd')}},
+        {"a": {"x": {"v": "utyu", "w": "asd"}, "y": 
'1999-04-19'?date('yyyy-MM-dd')}}]>
+<#list x?sortBy(['a', 'x', 'v']) as i>
+- ${i.a.x.v}
+</#list>
+
+Order by a.y, which is a date:
+<#list x?sortBy(['a', 'y']) as i>
+- ${i.a.y?string('yyyy-MM-dd')}
+</#list>
+
+Reverse:
+--------
+
+Order by weight desc:
+<#assign ls = ls?reverse>
+<#list ls as i>
+- ${i.name}: ${i.weight}
+</#list>
+
+Order by weight desc desc:
+<#assign ls = ls?reverse>
+<#list ls as i>
+- ${i.name}: ${i.weight}
+</#list>
+
+Order by weight desc desc desc:
+<#assign ls = ls?reverse>
+<#list ls as i>
+- ${i.name}: ${i.weight}
+</#list>
+
+Contains:
+---------
+
+<#macro test></#macro>
+<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, 
'1992-02-21'?date('yyyy-MM-dd')]>
+True:
+${x?seqContains(1.0)?string}
+${x?seqContains("2")?string}
+${x?seqContains(true)?string}
+${x?seqContains('1992-02-21'?date('yyyy-MM-dd'))?string}
+${abcSet?seqContains("a")?string}
+${abcSet?seqContains("b")?string}
+${abcSet?seqContains("c")?string}
+
+False:
+${x?seqContains("1")?string}
+${x?seqContains(2)?string}
+${x?seqContains(false)?string}
+${x?seqContains('1992-02-22'?date('yyyy-MM-dd'))?string}
+${abcSet?seqContains("A")?string}
+${abcSet?seqContains(1)?string}
+${abcSet?seqContains(true)?string}
+
+<#assign x = []>
+False: ${x?seqContains(1)?string}
+
+Index_of:
+---------
+
+<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, 
'1992-02-21'?date('yyyy-MM-dd')]>
+0 = ${x?seqIndexOf(1.0)}
+1 = ${x?seqIndexOf("2")}
+2 = ${x?seqIndexOf(true)}
+6 = ${x?seqIndexOf('1992-02-21'?date('yyyy-MM-dd'))}
+0 = ${abcSet?seqIndexOf("a")}
+1 = ${abcSet?seqIndexOf("b")}
+2 = ${abcSet?seqIndexOf("c")}
+
+-1 = ${x?seqIndexOf("1")}
+-1 = ${x?seqIndexOf(2)}
+-1 = ${x?seqIndexOf(false)}
+-1 = ${x?seqIndexOf('1992-02-22'?date('yyyy-MM-dd'))}
+-1 = ${abcSet?seqIndexOf("A")}
+-1 = ${abcSet?seqIndexOf(1)}
+-1 = ${abcSet?seqIndexOf(true)}
+
+<#assign x = []>
+-1 = ${x?seqIndexOf(1)}
+
+Last_index_of:
+--------------
+
+<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, 1, 
'1992-02-21'?date('yyyy-MM-dd')]>
+6 = ${x?seqLastIndexOf(1.0)}
+1 = ${x?seqLastIndexOf("2")}
+2 = ${x?seqLastIndexOf(true)}
+7 = ${x?seqLastIndexOf('1992-02-21'?date('yyyy-MM-dd'))}
+-1 = ${x?seqLastIndexOf("1")}
+0 = ${abcSet?seqLastIndexOf("a")}
+1 = ${abcSet?seqLastIndexOf("b")}
+2 = ${abcSet?seqLastIndexOf("c")}
+-1 = ${abcSet?seqLastIndexOf("A")}
+
+Index_of and last_index_of with starting indices
+------------------------------------------------
+
+<#assign names = ["Joe", "Fred", "Joe", "Susan"]>
+seq_index_of "Joe":
+0 = ${names?seqIndexOf("Joe", -2)}
+0 = ${names?seqIndexOf("Joe", -1)}
+0 = ${names?seqIndexOf("Joe", 0)}
+2 = ${names?seqIndexOf("Joe", 1)}
+2 = ${names?seqIndexOf("Joe", 2)}
+-1 = ${names?seqIndexOf("Joe", 3)}
+-1 = ${names?seqIndexOf("Joe", 4)}
+ 
+seq_last_index_of "Joe":
+-1 = ${names?seqLastIndexOf("Joe", -2)}
+-1 = ${names?seqLastIndexOf("Joe", -1)}
+0 = ${names?seqLastIndexOf("Joe", 0)}
+0 = ${names?seqLastIndexOf("Joe", 1)}
+2 = ${names?seqLastIndexOf("Joe", 2)}
+2 = ${names?seqLastIndexOf("Joe", 3)}
+2 = ${names?seqLastIndexOf("Joe", 4)}
+ 
+seq_index_of "Susan":
+3 = ${names?seqIndexOf("Susan", -2)}
+3 = ${names?seqIndexOf("Susan", -1)}
+3 = ${names?seqIndexOf("Susan", 0)}
+3 = ${names?seqIndexOf("Susan", 1)}
+3 = ${names?seqIndexOf("Susan", 2)}
+3 = ${names?seqIndexOf("Susan", 3)}
+-1 = ${names?seqIndexOf("Susan", 4)}
+ 
+seq_last_index_of "Susan":
+-1 = ${names?seqLastIndexOf("Susan", -2)}
+-1 = ${names?seqLastIndexOf("Susan", -1)}
+-1 = ${names?seqLastIndexOf("Susan", 0)}
+-1 = ${names?seqLastIndexOf("Susan", 1)}
+-1 = ${names?seqLastIndexOf("Susan", 2)}
+3 = ${names?seqLastIndexOf("Susan", 3)}
+3 = ${names?seqLastIndexOf("Susan", 4)}
+
+seq_index_of "a":
+0 = ${abcSet?seqIndexOf("a", -2)}
+0 = ${abcSet?seqIndexOf("a", -1)}
+0 = ${abcSet?seqIndexOf("a", 0)}
+-1 = ${abcSet?seqIndexOf("a", 1)}
+-1 = ${abcSet?seqIndexOf("a", 2)}
+-1 = ${abcSet?seqIndexOf("a", 3)}
+-1 = ${abcSet?seqIndexOf("a", 4)}
+
+seq_index_of "b":
+1 = ${abcSet?seqIndexOf("b", -2)}
+1 = ${abcSet?seqIndexOf("b", -1)}
+1 = ${abcSet?seqIndexOf("b", 0)}
+1 = ${abcSet?seqIndexOf("b", 1)}
+-1 = ${abcSet?seqIndexOf("b", 2)}
+-1 = ${abcSet?seqIndexOf("b", 3)}
+
+seq_index_of "c":
+2 = ${abcSet?seqIndexOf("c", -2)}
+2 = ${abcSet?seqIndexOf("c", -1)}
+2 = ${abcSet?seqIndexOf("c", 0)}
+2 = ${abcSet?seqIndexOf("c", 1)}
+2 = ${abcSet?seqIndexOf("c", 2)}
+-1 = ${abcSet?seqIndexOf("c", 3)}
+ 
+seq_last_index_of "a":
+-1 = ${abcSet?seqLastIndexOf("a", -2)}
+-1 = ${abcSet?seqLastIndexOf("a", -1)}
+0 = ${abcSet?seqLastIndexOf("a", 0)}
+0 = ${abcSet?seqLastIndexOf("a", 1)}
+0 = ${abcSet?seqLastIndexOf("a", 2)}
+0 = ${abcSet?seqLastIndexOf("a", 3)}
+0 = ${abcSet?seqLastIndexOf("a", 4)}
+
+seq_last_index_of "b":
+-1 = ${abcSet?seqLastIndexOf("b", -2)}
+-1 = ${abcSet?seqLastIndexOf("b", -1)}
+-1 = ${abcSet?seqLastIndexOf("b", 0)}
+1 = ${abcSet?seqLastIndexOf("b", 1)}
+1 = ${abcSet?seqLastIndexOf("b", 2)}
+1 = ${abcSet?seqLastIndexOf("b", 3)}
+
+seq_last_index_of "c":
+-1 = ${abcSet?seqLastIndexOf("c", -2)}
+-1 = ${abcSet?seqLastIndexOf("c", -1)}
+-1 = ${abcSet?seqLastIndexOf("c", 0)}
+-1 = ${abcSet?seqLastIndexOf("c", 1)}
+2 = ${abcSet?seqLastIndexOf("c", 2)}
+2 = ${abcSet?seqLastIndexOf("c", 3)}
+
+Sequence builtins ignoring nulls
+--------------------------------
+
+true = ${listWithNull?seqContains('c')?string}
+2 = ${listWithNull?seqIndexOf('c')}
+0 = ${listWithNull?seqLastIndexOf('a')}
+
+These should throw exception, but for BC they don't:
+false = ${listWithNull?seqContains(noSuchVar)?string}
+-1 = ${listWithNull?seqIndexOf(noSuchVar)}
+-1 = ${listWithNull?seqLastIndexOf(noSuchVar)}
+
+Sequence built-ins failing on date-type mismatch
+------------------------------------------------
+
+<#assign x = ['1992-02-21'?date('yyyy-MM-dd'), 'foo']>
+<@assertEquals actual=x?seqIndexOf('foo') expected=1 />
+<@assertEquals actual=x?seqIndexOf('1992-02-21'?date('yyyy-MM-dd')) expected=0 
/>
+<@assertFails message="dates of different types">
+  0 = ${x?seqIndexOf('1992-02-21 00:00:00'?dateTime('yyyy-MM-dd HH:mm:ss'))}
+</@>
+
+Chunk
+-----
+
+<#assign ls = ['a', 'b', 'c', 'd', 'e', 'f', 'g']>
+<#list ['NULL', '-'] as fill>
+  <#list [1, 2, 3, 4, 5, 10] as columns>
+    <@printTable ls columns=columns fill=fill />
+  </#list>
+</#list>
+<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9] columns=3 fill='NULL' />
+<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9] columns=3 fill='-' />
+<@printTable [1] columns=3 fill='NULL' />
+<@printTable [1] columns=3 fill='-' />
+<@printTable [] columns=3 fill='NULL' />
+<@printTable [] columns=3 fill='-' />
+
+<#macro printTable ls{positional} columns fill>
+  columns = ${columns}, fill = ${fill}:<#lt>
+  <#if fill=='NULL'>
+    <#local rows = ls?chunk(columns)>
+  <#else>
+    <#local rows = ls?chunk(columns, fill)>
+  </#if>
+  Rows: ${rows?size}
+  <#list rows as row>
+    <#list row as i>${i} </#list>  <-- Columns: ${row?size}
+  </#list>
+  
+</#macro>
+
+
+Join
+----
+
+<#assign xs = [1, "two", "three", 4]>
+- ${xs?join(", ")}
+- ${[]?join(", ")}
+- ${xs?join(", ", "(empty)", ".")}
+- ${[]?join(", ", "(empty)", ".")}
+- ${listWithNull?join(", ")}
+- ${listWithNull?join(", ", "(empty)")}
+- ${listWithNull?join(", ", "(empty)", ".")}
+- ${listWithNullsOnly?join(", ")}
+- ${listWithNullsOnly?join(", ", "(empty)")}
+- ${listWithNullsOnly?join(", ", "(empty)", ".")}
+- ${abcSet?join(", ", "(empty)", ".")}
+- ${abcCollection?join(", ", "(empty)", ".")}
+<@assertFails message="index 1">${['a', [], 'c']?join(", ", "(empty)", 
".")}</@>
+
+Misc
+----
+
+First of set 1: ${abcSet?first}
+First of set 2: ${abcSetNonSeq?first}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
deleted file mode 100644
index b8cb070..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
+++ /dev/null
@@ -1,360 +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.
--->
-<@noOutput>
-<#setting locale="en_US">
-<#setting numberFormat="0.#########">
-
-<#assign ls = []?sort>
-<#list ls as i>
-- ${i}
-</#list>
-<@assertEquals expected=0 actual=ls?size />
-<@assertEquals expected=3 actual=set?size />
-</@noOutput>
-Sorting strings:
-----------------
-
-String order:
-<#assign ls = ["whale", "Barbara", "zeppelin", "aardvark", "beetroot"]?sort>
-<#list ls as i>
-- ${i}
-</#list>
-
-First: ${ls?first}
-Last: ${ls?last}
-Size ${ls?size}
-
-Numerical order:
-<#assign ls = [123?byte, 543, -324, -34?float, 0.11, 0, 111?int, 0.1?double, 
1, 5]?sort>
-<#list ls as i>
-- ${i}
-</#list>
-
-First: ${ls?first}
-Last: ${ls?last}
-Size ${ls?size}
-
-Date/time order:
-<#assign x = [
-        '08:05'?time('HH:mm'),
-        '18:00'?time('HH:mm'),
-        '06:05'?time('HH:mm'),
-        '08:15'?time('HH:mm')]>
-<#list x?sort as i>
-- ${i?string('HH:mm')}
-</#list>
-
-Boolean order:
-<#assign x = [
-        true,
-        false,
-        false,
-        true]>
-<#list x?sort as i>
-- ${i?string}
-</#list>
-
-
-Sorting hashes:
----------------
-
-<#assign ls = [
-  {"name":"whale", "weight":2000?short},
-  {"name":"Barbara", "weight":53},
-  {"name":"zeppelin", "weight":-200?float},
-  {"name":"aardvark", "weight":30?long},
-  {"name":"beetroot", "weight":0.3}
-]>
-Order by name:
-<#assign ls = ls?sortBy("name")>
-<#list ls as i>
-- ${i.name}: ${i.weight}
-</#list>
-
-Order by weight:
-<#assign ls = ls?sortBy("weight")>
-<#list ls as i>
-- ${i.name}: ${i.weight}
-</#list>
-
-Order by a.x.v:
-<#assign x = [
-        {"a": {"x": {"v": "qweqw", "w": "asd"}, "y": 
'1998-02-20'?date('yyyy-MM-dd')}},
-        {"a": {"x": {"v": "aqweqw", "w": "asd"}, "y": 
'1999-01-20'?date('yyyy-MM-dd')}},
-        {"a": {"x": {"v": "dfgdf", "w": "asd"}, "y": 
'1999-04-20'?date('yyyy-MM-dd')}},
-        {"a": {"x": {"v": "utyu", "w": "asd"}, "y": 
'1999-04-19'?date('yyyy-MM-dd')}}]>
-<#list x?sortBy(['a', 'x', 'v']) as i>
-- ${i.a.x.v}
-</#list>
-
-Order by a.y, which is a date:
-<#list x?sortBy(['a', 'y']) as i>
-- ${i.a.y?string('yyyy-MM-dd')}
-</#list>
-
-Reverse:
---------
-
-Order by weight desc:
-<#assign ls = ls?reverse>
-<#list ls as i>
-- ${i.name}: ${i.weight}
-</#list>
-
-Order by weight desc desc:
-<#assign ls = ls?reverse>
-<#list ls as i>
-- ${i.name}: ${i.weight}
-</#list>
-
-Order by weight desc desc desc:
-<#assign ls = ls?reverse>
-<#list ls as i>
-- ${i.name}: ${i.weight}
-</#list>
-
-Contains:
----------
-
-<#macro test></#macro>
-<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, 
'1992-02-21'?date('yyyy-MM-dd')]>
-True:
-${x?seqContains(1.0)?string}
-${x?seqContains("2")?string}
-${x?seqContains(true)?string}
-${x?seqContains('1992-02-21'?date('yyyy-MM-dd'))?string}
-${abcSet?seqContains("a")?string}
-${abcSet?seqContains("b")?string}
-${abcSet?seqContains("c")?string}
-
-False:
-${x?seqContains("1")?string}
-${x?seqContains(2)?string}
-${x?seqContains(false)?string}
-${x?seqContains('1992-02-22'?date('yyyy-MM-dd'))?string}
-${abcSet?seqContains("A")?string}
-${abcSet?seqContains(1)?string}
-${abcSet?seqContains(true)?string}
-
-<#assign x = []>
-False: ${x?seqContains(1)?string}
-
-Index_of:
----------
-
-<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, 
'1992-02-21'?date('yyyy-MM-dd')]>
-0 = ${x?seqIndexOf(1.0)}
-1 = ${x?seqIndexOf("2")}
-2 = ${x?seqIndexOf(true)}
-6 = ${x?seqIndexOf('1992-02-21'?date('yyyy-MM-dd'))}
-0 = ${abcSet?seqIndexOf("a")}
-1 = ${abcSet?seqIndexOf("b")}
-2 = ${abcSet?seqIndexOf("c")}
-
--1 = ${x?seqIndexOf("1")}
--1 = ${x?seqIndexOf(2)}
--1 = ${x?seqIndexOf(false)}
--1 = ${x?seqIndexOf('1992-02-22'?date('yyyy-MM-dd'))}
--1 = ${abcSet?seqIndexOf("A")}
--1 = ${abcSet?seqIndexOf(1)}
--1 = ${abcSet?seqIndexOf(true)}
-
-<#assign x = []>
--1 = ${x?seqIndexOf(1)}
-
-Last_index_of:
---------------
-
-<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, 1, 
'1992-02-21'?date('yyyy-MM-dd')]>
-6 = ${x?seqLastIndexOf(1.0)}
-1 = ${x?seqLastIndexOf("2")}
-2 = ${x?seqLastIndexOf(true)}
-7 = ${x?seqLastIndexOf('1992-02-21'?date('yyyy-MM-dd'))}
--1 = ${x?seqLastIndexOf("1")}
-0 = ${abcSet?seqLastIndexOf("a")}
-1 = ${abcSet?seqLastIndexOf("b")}
-2 = ${abcSet?seqLastIndexOf("c")}
--1 = ${abcSet?seqLastIndexOf("A")}
-
-Index_of and last_index_of with starting indices
-------------------------------------------------
-
-<#assign names = ["Joe", "Fred", "Joe", "Susan"]>
-seq_index_of "Joe":
-0 = ${names?seqIndexOf("Joe", -2)}
-0 = ${names?seqIndexOf("Joe", -1)}
-0 = ${names?seqIndexOf("Joe", 0)}
-2 = ${names?seqIndexOf("Joe", 1)}
-2 = ${names?seqIndexOf("Joe", 2)}
--1 = ${names?seqIndexOf("Joe", 3)}
--1 = ${names?seqIndexOf("Joe", 4)}
- 
-seq_last_index_of "Joe":
--1 = ${names?seqLastIndexOf("Joe", -2)}
--1 = ${names?seqLastIndexOf("Joe", -1)}
-0 = ${names?seqLastIndexOf("Joe", 0)}
-0 = ${names?seqLastIndexOf("Joe", 1)}
-2 = ${names?seqLastIndexOf("Joe", 2)}
-2 = ${names?seqLastIndexOf("Joe", 3)}
-2 = ${names?seqLastIndexOf("Joe", 4)}
- 
-seq_index_of "Susan":
-3 = ${names?seqIndexOf("Susan", -2)}
-3 = ${names?seqIndexOf("Susan", -1)}
-3 = ${names?seqIndexOf("Susan", 0)}
-3 = ${names?seqIndexOf("Susan", 1)}
-3 = ${names?seqIndexOf("Susan", 2)}
-3 = ${names?seqIndexOf("Susan", 3)}
--1 = ${names?seqIndexOf("Susan", 4)}
- 
-seq_last_index_of "Susan":
--1 = ${names?seqLastIndexOf("Susan", -2)}
--1 = ${names?seqLastIndexOf("Susan", -1)}
--1 = ${names?seqLastIndexOf("Susan", 0)}
--1 = ${names?seqLastIndexOf("Susan", 1)}
--1 = ${names?seqLastIndexOf("Susan", 2)}
-3 = ${names?seqLastIndexOf("Susan", 3)}
-3 = ${names?seqLastIndexOf("Susan", 4)}
-
-seq_index_of "a":
-0 = ${abcSet?seqIndexOf("a", -2)}
-0 = ${abcSet?seqIndexOf("a", -1)}
-0 = ${abcSet?seqIndexOf("a", 0)}
--1 = ${abcSet?seqIndexOf("a", 1)}
--1 = ${abcSet?seqIndexOf("a", 2)}
--1 = ${abcSet?seqIndexOf("a", 3)}
--1 = ${abcSet?seqIndexOf("a", 4)}
-
-seq_index_of "b":
-1 = ${abcSet?seqIndexOf("b", -2)}
-1 = ${abcSet?seqIndexOf("b", -1)}
-1 = ${abcSet?seqIndexOf("b", 0)}
-1 = ${abcSet?seqIndexOf("b", 1)}
--1 = ${abcSet?seqIndexOf("b", 2)}
--1 = ${abcSet?seqIndexOf("b", 3)}
-
-seq_index_of "c":
-2 = ${abcSet?seqIndexOf("c", -2)}
-2 = ${abcSet?seqIndexOf("c", -1)}
-2 = ${abcSet?seqIndexOf("c", 0)}
-2 = ${abcSet?seqIndexOf("c", 1)}
-2 = ${abcSet?seqIndexOf("c", 2)}
--1 = ${abcSet?seqIndexOf("c", 3)}
- 
-seq_last_index_of "a":
--1 = ${abcSet?seqLastIndexOf("a", -2)}
--1 = ${abcSet?seqLastIndexOf("a", -1)}
-0 = ${abcSet?seqLastIndexOf("a", 0)}
-0 = ${abcSet?seqLastIndexOf("a", 1)}
-0 = ${abcSet?seqLastIndexOf("a", 2)}
-0 = ${abcSet?seqLastIndexOf("a", 3)}
-0 = ${abcSet?seqLastIndexOf("a", 4)}
-
-seq_last_index_of "b":
--1 = ${abcSet?seqLastIndexOf("b", -2)}
--1 = ${abcSet?seqLastIndexOf("b", -1)}
--1 = ${abcSet?seqLastIndexOf("b", 0)}
-1 = ${abcSet?seqLastIndexOf("b", 1)}
-1 = ${abcSet?seqLastIndexOf("b", 2)}
-1 = ${abcSet?seqLastIndexOf("b", 3)}
-
-seq_last_index_of "c":
--1 = ${abcSet?seqLastIndexOf("c", -2)}
--1 = ${abcSet?seqLastIndexOf("c", -1)}
--1 = ${abcSet?seqLastIndexOf("c", 0)}
--1 = ${abcSet?seqLastIndexOf("c", 1)}
-2 = ${abcSet?seqLastIndexOf("c", 2)}
-2 = ${abcSet?seqLastIndexOf("c", 3)}
-
-Sequence builtins ignoring nulls
---------------------------------
-
-true = ${listWithNull?seqContains('c')?string}
-2 = ${listWithNull?seqIndexOf('c')}
-0 = ${listWithNull?seqLastIndexOf('a')}
-
-These should throw exception, but for BC they don't:
-false = ${listWithNull?seqContains(noSuchVar)?string}
--1 = ${listWithNull?seqIndexOf(noSuchVar)}
--1 = ${listWithNull?seqLastIndexOf(noSuchVar)}
-
-Sequence built-ins failing on date-type mismatch
-------------------------------------------------
-
-<#assign x = ['1992-02-21'?date('yyyy-MM-dd'), 'foo']>
-<@assertEquals actual=x?seqIndexOf('foo') expected=1 />
-<@assertEquals actual=x?seqIndexOf('1992-02-21'?date('yyyy-MM-dd')) expected=0 
/>
-<@assertFails message="dates of different types">
-  0 = ${x?seqIndexOf('1992-02-21 00:00:00'?dateTime('yyyy-MM-dd HH:mm:ss'))}
-</@>
-
-Chunk
------
-
-<#assign ls = ['a', 'b', 'c', 'd', 'e', 'f', 'g']>
-<#list ['NULL', '-'] as fill>
-  <#list [1, 2, 3, 4, 5, 10] as columns>
-    <@printTable ls columns=columns fill=fill />
-  </#list>
-</#list>
-<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9] columns=3 fill='NULL' />
-<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9] columns=3 fill='-' />
-<@printTable [1] columns=3 fill='NULL' />
-<@printTable [1] columns=3 fill='-' />
-<@printTable [] columns=3 fill='NULL' />
-<@printTable [] columns=3 fill='-' />
-
-<#macro printTable ls{positional} columns fill>
-  columns = ${columns}, fill = ${fill}:<#lt>
-  <#if fill=='NULL'>
-    <#local rows = ls?chunk(columns)>
-  <#else>
-    <#local rows = ls?chunk(columns, fill)>
-  </#if>
-  Rows: ${rows?size}
-  <#list rows as row>
-    <#list row as i>${i} </#list>  <-- Columns: ${row?size}
-  </#list>
-  
-</#macro>
-
-
-Join
-----
-
-<#assign xs = [1, "two", "three", 4]>
-- ${xs?join(", ")}
-- ${[]?join(", ")}
-- ${xs?join(", ", "(empty)", ".")}
-- ${[]?join(", ", "(empty)", ".")}
-- ${listWithNull?join(", ")}
-- ${listWithNull?join(", ", "(empty)")}
-- ${listWithNull?join(", ", "(empty)", ".")}
-- ${listWithNullsOnly?join(", ")}
-- ${listWithNullsOnly?join(", ", "(empty)")}
-- ${listWithNullsOnly?join(", ", "(empty)", ".")}
-- ${abcSet?join(", ", "(empty)", ".")}
-- ${abcCollection?join(", ", "(empty)", ".")}
-<@assertFails message="index 1">${['a', [], 'c']?join(", ", "(empty)", 
".")}</@>
-
-Misc
-----
-
-First of set 1: ${abcSet?first}
-First of set 2: ${abcSetNonSeq?first}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.f3ac
new file mode 100644
index 0000000..0febdef
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.f3ac
@@ -0,0 +1,53 @@
+<#--
+  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.
+-->
+<#setting locale='de_DE'>
+<@assertEquals expected='de_DE' actual=.locale />
+<@assertEquals expected='de' actual=.lang />
+<@assertEquals expected='java.util.Locale "de_DE"' 
actual=javaObjectInfo.info(.localeObject) />
+
+<#setting numberFormat="'f'#">
+<@assertEquals expected='f1' actual=1?string />
+
+<#setting booleanFormat="t,f">
+<@assertEquals expected='t' actual=true?string />
+
+<#setting dateFormat="'df'">
+<@assertEquals expected='df' actual=.now?date?string />
+
+<#setting timeFormat="'tf'">
+<@assertEquals expected='tf' actual=.now?time?string />
+
+<#setting dateTimeFormat="'dtf'">
+<@assertEquals expected='dtf' actual=.now?string />
+
+<#setting timeZone='GMT+00'>
+<#assign t1='2000'?dateTime('yyyy')>
+<#setting timeZone='GMT+01'>
+<#assign t2='2000'?dateTime('yyyy')>
+<@assertEquals expected=1000*60*60 actual=t1?long-t2?long />
+
+<#setting sqlDateAndTimeTimeZone='GMT+01'>
+
+<#setting urlEscapingCharset='ISO-8859-1'>
+<@assertEquals expected='%E1' actual='á'?url />
+<#setting urlEscapingCharset='UTF-8'>
+<@assertEquals expected='%C3%A1' actual='á'?url />
+
+<#setting outputEncoding="ISO-8859-2">
+<@assertEquals expected="ISO-8859-2" actual=.outputEncoding />

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.ftl
deleted file mode 100644
index 0febdef..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/setting.ftl
+++ /dev/null
@@ -1,53 +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.
--->
-<#setting locale='de_DE'>
-<@assertEquals expected='de_DE' actual=.locale />
-<@assertEquals expected='de' actual=.lang />
-<@assertEquals expected='java.util.Locale "de_DE"' 
actual=javaObjectInfo.info(.localeObject) />
-
-<#setting numberFormat="'f'#">
-<@assertEquals expected='f1' actual=1?string />
-
-<#setting booleanFormat="t,f">
-<@assertEquals expected='t' actual=true?string />
-
-<#setting dateFormat="'df'">
-<@assertEquals expected='df' actual=.now?date?string />
-
-<#setting timeFormat="'tf'">
-<@assertEquals expected='tf' actual=.now?time?string />
-
-<#setting dateTimeFormat="'dtf'">
-<@assertEquals expected='dtf' actual=.now?string />
-
-<#setting timeZone='GMT+00'>
-<#assign t1='2000'?dateTime('yyyy')>
-<#setting timeZone='GMT+01'>
-<#assign t2='2000'?dateTime('yyyy')>
-<@assertEquals expected=1000*60*60 actual=t1?long-t2?long />
-
-<#setting sqlDateAndTimeTimeZone='GMT+01'>
-
-<#setting urlEscapingCharset='ISO-8859-1'>
-<@assertEquals expected='%E1' actual='á'?url />
-<#setting urlEscapingCharset='UTF-8'>
-<@assertEquals expected='%C3%A1' actual='á'?url />
-
-<#setting outputEncoding="ISO-8859-2">
-<@assertEquals expected="ISO-8859-2" actual=.outputEncoding />

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.f3ac
new file mode 100644
index 0000000..07a3f1e
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.f3ac
@@ -0,0 +1,44 @@
+<#--
+  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.
+-->
+<@assertEquals expected="string" actual=mStringC.c />
+<@assertEquals expected=1 actual=mStringC?keys?size />
+<@assertEquals expected="null" actual=mStringC.d!'null' />
+<@assertEquals expected=1 actual=mStringC?keys?size />
+
+<@assertEquals expected="null" actual=mStringCNull.c!'null' />
+<@assertEquals expected=1 actual=mStringCNull?keys?size />
+<@assertEquals expected="null" actual=mStringCNull.d!'null' />
+<@assertEquals expected=1 actual=mStringCNull?keys?size />
+
+<@assertEquals expected="char" actual=mCharC.c />
+<@assertEquals expected=1 actual=mCharC?keys?size />
+<@assertEquals expected="null" actual=mCharC.d!'null' />
+<@assertEquals expected=1 actual=mCharC?keys?size />
+
+<@assertEquals expected="null" actual=mCharCNull.c!'null' />
+<@assertEquals expected=1 actual=mCharCNull?keys?size />
+<@assertEquals expected="null" actual=mCharCNull.d!'null' />
+<@assertEquals expected=1 actual=mCharCNull?keys?size />
+
+<@assertEquals expected="char" actual=mMixed.c />
+<@assertEquals expected="string" actual=mMixed.s />
+<@assertEquals expected="string2" actual=mMixed.s2 />
+<@assertEquals expected="null" actual=mMixed.s2n!'null' />
+<@assertEquals expected="null" actual=mMixed.wrong!'null' />
+<@assertEquals expected=4 actual=mMixed?keys?size />

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.ftl
deleted file mode 100644
index 07a3f1e..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/simplehash-char-key.ftl
+++ /dev/null
@@ -1,44 +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.
--->
-<@assertEquals expected="string" actual=mStringC.c />
-<@assertEquals expected=1 actual=mStringC?keys?size />
-<@assertEquals expected="null" actual=mStringC.d!'null' />
-<@assertEquals expected=1 actual=mStringC?keys?size />
-
-<@assertEquals expected="null" actual=mStringCNull.c!'null' />
-<@assertEquals expected=1 actual=mStringCNull?keys?size />
-<@assertEquals expected="null" actual=mStringCNull.d!'null' />
-<@assertEquals expected=1 actual=mStringCNull?keys?size />
-
-<@assertEquals expected="char" actual=mCharC.c />
-<@assertEquals expected=1 actual=mCharC?keys?size />
-<@assertEquals expected="null" actual=mCharC.d!'null' />
-<@assertEquals expected=1 actual=mCharC?keys?size />
-
-<@assertEquals expected="null" actual=mCharCNull.c!'null' />
-<@assertEquals expected=1 actual=mCharCNull?keys?size />
-<@assertEquals expected="null" actual=mCharCNull.d!'null' />
-<@assertEquals expected=1 actual=mCharCNull?keys?size />
-
-<@assertEquals expected="char" actual=mMixed.c />
-<@assertEquals expected="string" actual=mMixed.s />
-<@assertEquals expected="string2" actual=mMixed.s2 />
-<@assertEquals expected="null" actual=mMixed.s2n!'null' />
-<@assertEquals expected="null" actual=mMixed.wrong!'null' />
-<@assertEquals expected=4 actual=mMixed?keys?size />

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.f3ac
new file mode 100644
index 0000000..b67991f
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.f3ac
@@ -0,0 +1,38 @@
+<#--
+  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.
+-->
+<#-- Mostly just checks if the expressions doesn't fail -->
+<#assign works = .dataModel>
+<#attempt>
+  ${noSuchVariableExists}
+<#recover>
+  <#assign works = .error>
+</#attempt>
+<#assign works = .globals>
+${.lang} == en
+${.locale} == en_US
+<#assign works = .locals!>
+<#assign works = .main>
+<#assign works = .node!>
+${.outputEncoding?lowerCase} == utf-8
+${.currentTemplateName} == specialvars.f3ac
+${.urlEscapingCharset?lowerCase} == iso-8859-1
+<#assign foo = "x">
+${.vars['foo']} == x
+<#assign works = .version>
+${.now?isDatetime?c} == true
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.ftl
deleted file mode 100644
index c6b608f..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/specialvars.ftl
+++ /dev/null
@@ -1,38 +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.
--->
-<#-- Mostly just checks if the expressions doesn't fail -->
-<#assign works = .dataModel>
-<#attempt>
-  ${noSuchVariableExists}
-<#recover>
-  <#assign works = .error>
-</#attempt>
-<#assign works = .globals>
-${.lang} == en
-${.locale} == en_US
-<#assign works = .locals!>
-<#assign works = .main>
-<#assign works = .node!>
-${.outputEncoding?lowerCase} == utf-8
-${.currentTemplateName} == specialvars.ftl
-${.urlEscapingCharset?lowerCase} == iso-8859-1
-<#assign foo = "x">
-${.vars['foo']} == x
-<#assign works = .version>
-${.now?isDatetime?c} == true
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.f3ac
new file mode 100644
index 0000000..40bb4ad
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.f3ac
@@ -0,0 +1,34 @@
+<#--
+  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.
+-->
+Was broken 2.3.19:
+<#setting numberFormat="0.#">
+<@assert 1232?contains('2') />
+<@assert 1232?indexOf('2') == 1 />
+<@assert 1232?lastIndexOf('2') == 3 />
+<@assert 1232?leftPad(6) == '  1232' /><@assert 1232?leftPad(6, '0') == 
'001232' />
+<@assert 1232?rightPad(6) == '1232  ' /><@assert 1232?rightPad(6, '0') == 
'123200' />
+<@assert 1232?matches('[1-3]+') />
+<@assert 1232?replace('2', 'z') == '1z3z' />
+<@assert 1232?replace('2', 'z', 'r') == '1z3z' />
+<@assert 1232?split('2')[1] == '3' /><@assert 1232?split('2')[2] == '' />
+<@assert 1232?split('2', 'r')[1] == '3' />
+
+Was no broken in 2.3.19:
+<@assert 1232?startsWith('12') />
+<@assert 1232?endsWith('32') />

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
deleted file mode 100644
index 40bb4ad..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
+++ /dev/null
@@ -1,34 +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.
--->
-Was broken 2.3.19:
-<#setting numberFormat="0.#">
-<@assert 1232?contains('2') />
-<@assert 1232?indexOf('2') == 1 />
-<@assert 1232?lastIndexOf('2') == 3 />
-<@assert 1232?leftPad(6) == '  1232' /><@assert 1232?leftPad(6, '0') == 
'001232' />
-<@assert 1232?rightPad(6) == '1232  ' /><@assert 1232?rightPad(6, '0') == 
'123200' />
-<@assert 1232?matches('[1-3]+') />
-<@assert 1232?replace('2', 'z') == '1z3z' />
-<@assert 1232?replace('2', 'z', 'r') == '1z3z' />
-<@assert 1232?split('2')[1] == '3' /><@assert 1232?split('2')[2] == '' />
-<@assert 1232?split('2', 'r')[1] == '3' />
-
-Was no broken in 2.3.19:
-<@assert 1232?startsWith('12') />
-<@assert 1232?endsWith('32') />

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.f3ac
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.f3ac
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.f3ac
new file mode 100644
index 0000000..f2a10dc
--- /dev/null
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.f3ac
@@ -0,0 +1,118 @@
+<#--
+  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.
+-->
+<#assign input>
+L16
+L27
+L38
+L49
+</#assign>
+
+List mode:
+<#assign matches = input?matches(".+") >
+Size: ${matches?size}
+<#list 0..<matches?size as i>[${matches[i]}]</#list>
+<#list matches as m>[${m}]</#list>
+
+Iterator mode:
+<#assign matches = input?matches(".+") >
+<#list matches as m>[${m}]</#list>
+<#list matches as m>[${m}(${matches?join(', ')})]</#list>
+<#list matches as m>[${m}]</#list>
+
+Iterator mode changes to list mode:
+<#assign matches = input?matches(".+") >
+<#list matches as m>[${m}]/${matches?size}</#list>
+<#list matches as m>[${m}]</#list>
+
+Iterator mode changes to list mode 2:
+<#assign matches = input?matches(".+") >
+<#list matches as m>[${m}]</#list>
+<#list matches as m>[${m}]/${matches?size}<#t></#list>
+
+List mode with embedded iteration:
+<#assign matches = input?matches(".+") >
+<#list 0..<matches?size as i>[${matches[i]}(${matches?join(', ')})]</#list>
+
+Entire input match:
+<#assign matches = input?matches(r".*(\d)(\d)") >
+<#assign firstGS = false>
+<#list matches as m>
+- M: ${m}
+    <#if firstGS?isBoolean>
+      <#assign firstGS = m?groups>
+    </#if>
+    <#list m?groups as g>
+    - G: ${g}
+    </#list>
+</#list>
+firstGS was: ${firstGS?join(', ')}
+
+Entire input match 2:
+<#assign match = "x12"?matches(r"x(\d)(\d)") >
+Matches: ${match?c}
+<#list match?groups as g>
+- G: ${g}
+</#list>
+As list:
+<#list match as m>
+- M: ${m}
+  <#list m?groups as g>
+    - G: ${g}
+  </#list>
+</#list>
+Groups again:
+<#list match?groups as g>
+- G: ${g}
+</#list>
+
+Entire input match 3:
+<#assign match = "x12"?matches(r"y(\d)(\d)") >
+Matches: ${match?c}
+<@assertEquals expected=3 actual=match?groups?size />
+<@assertEquals expected=0 actual=match?size />
+
+Entire input match 4:
+<#assign match = "x12"?matches(r"x(\d)(\d)") >
+Matches: ${match?c}
+<#assign gs = match?groups>
+<@assertEquals expected=3 actual=gs?size />
+<@assertEquals expected=1 actual=match?size />
+- G: ${gs[0]}
+- G: ${match?groups[1]}
+- G: ${gs[2]}
+
+Substring match nested into entire input match:
+<#assign match = "x12"?matches(r"x(\d)(\d)") >
+<#list match?groups as g>
+- G: ${g} (<#list match as m>[${m}{${m?groups?join(', ')}}]</#list>)
+</#list>
+
+Different entire input and substring matches:
+<#assign match = "123"?matches(r"(\d+?)") >
+${match?groups?join(", ")}
+<#list match as m>
+- M: ${m} (Gs: ${m?groups?join(", ")})
+</#list>
+
+Different entire input and substring matches 2:
+<#assign match = "123"?matches(r"\d+?") >
+${match?groups?join(", ")}
+<#list match as m>
+- M: ${m} (Gs: ${m?groups?join(", ")})
+</#list>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/freemarker/blob/81b48221/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.ftl
deleted file mode 100644
index f2a10dc..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps-matches.ftl
+++ /dev/null
@@ -1,118 +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.
--->
-<#assign input>
-L16
-L27
-L38
-L49
-</#assign>
-
-List mode:
-<#assign matches = input?matches(".+") >
-Size: ${matches?size}
-<#list 0..<matches?size as i>[${matches[i]}]</#list>
-<#list matches as m>[${m}]</#list>
-
-Iterator mode:
-<#assign matches = input?matches(".+") >
-<#list matches as m>[${m}]</#list>
-<#list matches as m>[${m}(${matches?join(', ')})]</#list>
-<#list matches as m>[${m}]</#list>
-
-Iterator mode changes to list mode:
-<#assign matches = input?matches(".+") >
-<#list matches as m>[${m}]/${matches?size}</#list>
-<#list matches as m>[${m}]</#list>
-
-Iterator mode changes to list mode 2:
-<#assign matches = input?matches(".+") >
-<#list matches as m>[${m}]</#list>
-<#list matches as m>[${m}]/${matches?size}<#t></#list>
-
-List mode with embedded iteration:
-<#assign matches = input?matches(".+") >
-<#list 0..<matches?size as i>[${matches[i]}(${matches?join(', ')})]</#list>
-
-Entire input match:
-<#assign matches = input?matches(r".*(\d)(\d)") >
-<#assign firstGS = false>
-<#list matches as m>
-- M: ${m}
-    <#if firstGS?isBoolean>
-      <#assign firstGS = m?groups>
-    </#if>
-    <#list m?groups as g>
-    - G: ${g}
-    </#list>
-</#list>
-firstGS was: ${firstGS?join(', ')}
-
-Entire input match 2:
-<#assign match = "x12"?matches(r"x(\d)(\d)") >
-Matches: ${match?c}
-<#list match?groups as g>
-- G: ${g}
-</#list>
-As list:
-<#list match as m>
-- M: ${m}
-  <#list m?groups as g>
-    - G: ${g}
-  </#list>
-</#list>
-Groups again:
-<#list match?groups as g>
-- G: ${g}
-</#list>
-
-Entire input match 3:
-<#assign match = "x12"?matches(r"y(\d)(\d)") >
-Matches: ${match?c}
-<@assertEquals expected=3 actual=match?groups?size />
-<@assertEquals expected=0 actual=match?size />
-
-Entire input match 4:
-<#assign match = "x12"?matches(r"x(\d)(\d)") >
-Matches: ${match?c}
-<#assign gs = match?groups>
-<@assertEquals expected=3 actual=gs?size />
-<@assertEquals expected=1 actual=match?size />
-- G: ${gs[0]}
-- G: ${match?groups[1]}
-- G: ${gs[2]}
-
-Substring match nested into entire input match:
-<#assign match = "x12"?matches(r"x(\d)(\d)") >
-<#list match?groups as g>
-- G: ${g} (<#list match as m>[${m}{${m?groups?join(', ')}}]</#list>)
-</#list>
-
-Different entire input and substring matches:
-<#assign match = "123"?matches(r"(\d+?)") >
-${match?groups?join(", ")}
-<#list match as m>
-- M: ${m} (Gs: ${m?groups?join(", ")})
-</#list>
-
-Different entire input and substring matches 2:
-<#assign match = "123"?matches(r"\d+?") >
-${match?groups?join(", ")}
-<#list match as m>
-- M: ${m} (Gs: ${m?groups?join(", ")})
-</#list>
\ No newline at end of file

Reply via email to