Continued work on the FM2 to FM3 converter
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/1f086e3f Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/1f086e3f Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/1f086e3f Branch: refs/heads/3 Commit: 1f086e3f243e431408f613cbbc8b5f5d2831c18f Parents: af17c34 Author: ddekany <ddek...@apache.org> Authored: Sun Jul 2 19:09:43 2017 +0200 Committer: ddekany <ddek...@apache.org> Committed: Sun Jul 2 19:09:43 2017 +0200 ---------------------------------------------------------------------- .../core/FM2ASTToFM3SourceConverter.java | 233 ++++++++++++++----- .../converter/FM2ToFM3ConverterTest.java | 68 ++++-- 2 files changed, 215 insertions(+), 86 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1f086e3f/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java ---------------------------------------------------------------------- diff --git a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java index b88f9f2..b1f221c 100644 --- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java +++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java @@ -153,7 +153,7 @@ public class FM2ASTToFM3SourceConverter { Expression content = getParam(node, 0, ParameterRole.CONTENT, Expression.class); int pos = getPositionAfterWSAndExpComments(getEndPositionExclusive(content)); - assertNodeContent(pos < src.length(), node, "Unexpected EOF", null); + assertNodeContent(pos < src.length(), node, "Unexpected EOF"); char c = src.charAt(pos); assertNodeContent(c == ';' || c == '}', node, "Expected ';' or '}', found {}", c); if (c == ';') { // #{exp; m1M2} -> ${exp?string('0.0#')} @@ -216,11 +216,128 @@ public class FM2ASTToFM3SourceConverter { printDirCustom((UnifiedCall) node); } else if (node instanceof Macro) { printDirMacroOrFunction((Macro) node); + } else if (node instanceof Assignment) { + printDirAssignmentLonely((Assignment) node); + } else if (node instanceof AssignmentInstruction) { + printDirAssignmentMultiple((AssignmentInstruction) node); + } else if (node instanceof AttemptBlock) { + printDirAttemptRecover((AttemptBlock) node); } else { throw new ConverterException("Unhandled AST TemplateElement class: " + node.getClass().getName()); } } + private void printDirAttemptRecover(AttemptBlock node) throws ConverterException { + print(tagBeginChar); + print("#attempt"); + printStartTagSkippedTokens(node, null, true); + print(tagEndChar); + + printNode(node.getChild(0)); + assertNodeContent(node.getChild(1) instanceof RecoveryBlock, node, "child[1] should be #recover"); + + RecoveryBlock recoverDir = getOnlyParam(node, ParameterRole.ERROR_HANDLER, RecoveryBlock.class); + print(tagBeginChar); + print("#recover"); + printStartTagSkippedTokens(recoverDir, null, true); + print(tagEndChar); + + printChildrenElements(recoverDir); + + print(tagBeginChar); + print("/#attempt"); // in FM2 this could be /#recover, but we normalize it + printEndTagSkippedTokens(node); + print(tagEndChar); + } + + private void printDirAssignmentMultiple(AssignmentInstruction node) throws ConverterException { + assertParamCount(node, 2); + + int pos = printDirAssignmentCommonTagTillAssignmentExp(node, 0); + + int childCnt = node.getChildCount(); + for (int childIdx = 0; childIdx < childCnt; childIdx++) { + Assignment assignment = (Assignment) node.getChild(childIdx); + pos = printDirAssignmentCommonExp(assignment, pos); + if (childIdx != childCnt - 1) { + pos = printWSAndExpComments(pos, ",", true); + } + } + + printDirAssignmentCommonTagAfterLastAssignmentExp(node, 1, pos); + } + + private void printDirAssignmentLonely(Assignment node) throws ConverterException { + assertParamCount(node, 5); + + int pos = printDirAssignmentCommonTagTillAssignmentExp(node, 3); + pos = printDirAssignmentCommonExp(node, pos); + printDirAssignmentCommonTagAfterLastAssignmentExp(node, 4, pos); + } + + private void printDirAssignmentCommonTagAfterLastAssignmentExp(TemplateElement node, int nsParamIdx, int pos) throws + ConverterException { + Expression ns = getParam(node, nsParamIdx, ParameterRole.NAMESPACE, Expression.class); + if (ns != null) { + pos = printWSAndExpComments(pos, "in", false); + printExp(ns); + pos = getEndPositionExclusive(ns); + } + pos = printWSAndExpComments(pos); + + char c = src.charAt(pos); + assertNodeContent(c == tagEndChar, node, "End of tag was expected, but found {}", c); + print(tagEndChar); + } + + private int printDirAssignmentCommonTagTillAssignmentExp(TemplateElement node, int scopeParamIdx) + throws ConverterException { + print(tagBeginChar); + + int scope = getParam(node, scopeParamIdx, ParameterRole.VARIABLE_SCOPE, Integer.class); + String tagName; + if (scope == Assignment.NAMESPACE) { + tagName = "#assign"; + } else if (scope == Assignment.GLOBAL) { + tagName = "#global"; + } else if (scope == Assignment.LOCAL) { + tagName = "#local"; + } else { + throw new UnexpectedNodeContentException(node, "Unhandled scope: {}", scope); + } + print(tagName); + int pos = getPositionAfterIdentifier(getStartPosition(node) + 2); + + pos = printWSAndExpComments(pos); + return pos; + } + + private int printDirAssignmentCommonExp(Assignment node, int pos) throws ConverterException { + { + String target = getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class); + print(FTLUtil.escapeIdentifier(target)); + pos = getPositionAfterIdentifier(pos); + } + + pos = printWSAndExpComments(pos); + + { + String operator = getParam(node, 1, ParameterRole.ASSIGNMENT_OPERATOR, String.class); + print(operator); + pos += operator.length(); + } + + pos = printWSAndExpComments(pos); + + Expression source = getParam(node, 2, ParameterRole.ASSIGNMENT_SOURCE, Expression.class); + if (source != null) { + printExp(source); + pos = getEndPositionExclusive(source); + } + + return pos; + } + private void printDirMacroOrFunction(Macro node) throws ConverterException { int paramCnt = node.getParameterCount(); @@ -236,30 +353,19 @@ public class FM2ASTToFM3SourceConverter { print(tagBeginChar); print(tagName); - int pos = getStartPosition(node) + 1; - assertNodeContent(src.substring(pos, pos + tagName.length()).equals(tagName), node, - "Tag name doesn't match {}", tagName); - pos += tagName.length(); + int pos = getPositionAfterIdentifier(getStartPosition(node) + 2); - { - String sep = readWSAndExpComments(pos); - printWithConvertedExpComments(sep); - pos += sep.length(); - } + pos = printWSAndExpComments(pos); String assignedName = getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class); print(FTLUtil.escapeIdentifier(assignedName)); { int lastPos = pos; pos = getPositionAfterIdentifier(pos); - assertNodeContent(pos > lastPos, node, "Expected target name", null); + assertNodeContent(pos > lastPos, node, "Expected target name"); } - { - String sep = readWSAndExpComments(pos, '(', true); - printWithConvertedExpComments(sep); - pos += sep.length(); - } + pos = printWSAndExpComments(pos, "(", true); int paramIdx = 1; while (node.getParameterRole(paramIdx) == ParameterRole.PARAMETER_NAME) { @@ -268,22 +374,17 @@ public class FM2ASTToFM3SourceConverter { { int lastPos = pos; pos = getPositionAfterIdentifier(pos); - assertNodeContent(pos > lastPos, node, "Expected parameter name", null); + assertNodeContent(pos > lastPos, node, "Expected parameter name"); } Expression paramDefault = getParam(node, paramIdx++, ParameterRole.PARAMETER_DEFAULT, Expression.class); if (paramDefault != null) { - String sep = readWSAndExpComments(pos, '=', false); - printWithConvertedExpComments(sep); + printWSAndExpComments(pos, "=", false); printExp(paramDefault); pos = getEndPositionExclusive(paramDefault); } - { - String sep = readWSAndExpComments(pos); - printWithConvertedExpComments(sep); - pos += sep.length(); - } + pos = printWSAndExpComments(pos); { char c = src.charAt(pos); assertNodeContent( @@ -295,13 +396,11 @@ public class FM2ASTToFM3SourceConverter { print(c); pos++; - String sep = readWSAndExpComments(pos); - printWithConvertedExpComments(sep); - pos += sep.length(); + pos = printWSAndExpComments(pos); } if (c == ')') { assertNodeContent(node.getParameterRole(paramIdx) != ParameterRole.PARAMETER_NAME, node, - "Expected no parameter after \"(\"", null); + "Expected no parameter after \"(\""); } } } @@ -318,15 +417,11 @@ public class FM2ASTToFM3SourceConverter { int lastPos = pos; pos = getPositionAfterIdentifier(pos); assertNodeContent(pos > lastPos, node, - "Expected catch-all parameter name", null); - } - { - String sep = readWSAndExpComments(pos); - printWithConvertedExpComments(sep); - pos += sep.length(); + "Expected catch-all parameter name"); } + pos = printWSAndExpComments(pos); assertNodeContent(src.startsWith("...", pos), node, - "Expected \"...\" after catch-all parameter name", null); + "Expected \"...\" after catch-all parameter name"); print("..."); pos += 3; } @@ -334,12 +429,8 @@ public class FM2ASTToFM3SourceConverter { assertNodeContent(paramIdx == paramCnt - 1, node, "Expected AST parameter at index {} to be the last one", paramIdx); - { - String sep = readWSAndExpComments(pos, ')', true); - printWithConvertedExpComments(sep); - pos += sep.length(); - } - assertNodeContent(src.charAt(pos) == tagEndChar, node, "Tag end not found", null); + pos = printWSAndExpComments(pos, ")", true); + assertNodeContent(src.charAt(pos) == tagEndChar, node, "Tag end not found"); print(tagEndChar); printChildrenElements(node); @@ -389,9 +480,9 @@ public class FM2ASTToFM3SourceConverter { int pos = getEndPositionExclusive(lastPrintedExp); boolean beforeFirstLoopVar = true; while (paramIdx < paramCount) { - String sep = readWSAndExpComments(pos, beforeFirstLoopVar ? ';' : ',', false); + String sep = readWSAndExpComments(pos, beforeFirstLoopVar ? ";" : ",", false); assertNodeContent(sep.length() != 0, node, - "Can't find loop variable separator", null); + "Can't find loop variable separator"); printWithConvertedExpComments(sep); pos += sep.length(); @@ -399,7 +490,7 @@ public class FM2ASTToFM3SourceConverter { print(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName)); String identifierInSrc = readIdentifier(pos); assertNodeContent(identifierInSrc.length() != 0, node, - "Can't find loop variable identifier in source", null); + "Can't find loop variable identifier in source"); pos += identifierInSrc.length(); // skip loop var name beforeFirstLoopVar = false; @@ -417,7 +508,7 @@ public class FM2ASTToFM3SourceConverter { } if (startTagEndPos != elementEndPos) { // We have an end-tag assertNodeContent(src.charAt(startTagEndPos - 1) != '/', node, - "Not expected \"/\" at the end of the start tag", null); + "Not expected \"/\" at the end of the start tag"); printChildrenElements(node); print(tagBeginChar); @@ -427,15 +518,15 @@ public class FM2ASTToFM3SourceConverter { nameStartPos--; } assertNodeContent(nameStartPos >= 2, node, - "Couldn't extract name from end-tag.", null); + "Couldn't extract name from end-tag."); // Also prints ignored WS after name, for now: printWithConvertedExpComments(src.substring(nameStartPos, elementEndPos)); print(tagEndChar); } else { // We don't have end-tag assertNodeContent(src.charAt(startTagEndPos - 1) == '/', node, - "Expected \"/\" at the end of the start tag", null); + "Expected \"/\" at the end of the start tag"); assertNodeContent(node.getChildCount() == 0, node, - "Expected no children", null); + "Expected no children"); } } @@ -649,9 +740,10 @@ public class FM2ASTToFM3SourceConverter { private void printExpBuiltinVariable(BuiltinVariable node) throws ConverterException { int startPos = getStartPosition(node); - String sep = readWSAndExpComments(startPos, '.', false); - printWithConvertedExpComments(sep); - String name = src.substring(startPos + sep.length(), getEndPositionExclusive(node)); + + int varNameStart = printWSAndExpComments(startPos, ".", false); + + String name = src.substring(varNameStart, getEndPositionExclusive(node)); print(convertBuiltInVariableName(name)); } @@ -668,8 +760,7 @@ public class FM2ASTToFM3SourceConverter { Expression lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class); String rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, String.class); printNode(lho); - printWithConvertedExpComments( - readWSAndExpComments(getEndPositionExclusive(lho), '.', false)); + printWSAndExpComments(getEndPositionExclusive(lho), ".", false); print(FTLUtil.escapeIdentifier(rho)); } @@ -707,9 +798,9 @@ public class FM2ASTToFM3SourceConverter { int openCharPos = getStartPosition(node); int closeCharPos = getEndPositionInclusive(node); assertNodeContent(src.charAt(openCharPos) == '{', node, - "Expected '{'", null); + "Expected '{'"); assertNodeContent(src.charAt(closeCharPos) == '}', node, - "Expected '}'", null); + "Expected '}'"); int paramCnt = node.getParameterCount(); if (paramCnt == 0) { @@ -740,9 +831,9 @@ public class FM2ASTToFM3SourceConverter { int openCharPos = getStartPosition(node); int closeCharPos = getEndPositionInclusive(node); assertNodeContent(src.charAt(openCharPos) == '[', node, - "Expected '['", null); + "Expected '['"); assertNodeContent(src.charAt(closeCharPos) == ']', node, - "Expected ']'", null); + "Expected ']'"); int paramCnt = node.getParameterCount(); if (paramCnt == 0) { @@ -1064,7 +1155,7 @@ public class FM2ASTToFM3SourceConverter { } assertNodeContent(pos > 0 && isCoreNameChar(src.charAt(pos)), node, - "Can't find end tag name", null); + "Can't find end tag name"); printWithConvertedExpComments(src.substring(pos + 1, tagEndPos)); } @@ -1134,6 +1225,11 @@ public class FM2ASTToFM3SourceConverter { } private void assertNodeContent(boolean good, TemplateObject node, String + errorMessage) throws UnexpectedNodeContentException { + assertNodeContent(good, node, errorMessage, null); + } + + private void assertNodeContent(boolean good, TemplateObject node, String errorMessage, Object msgParam) throws UnexpectedNodeContentException { if (!good) { throw new UnexpectedNodeContentException(node, errorMessage, msgParam); @@ -1220,21 +1316,36 @@ public class FM2ASTToFM3SourceConverter { return src.substring(startPos, getPositionAfterWSAndExpComments(startPos)); } - private String readWSAndExpComments(int startPos, char separator, boolean separatorOptional) + private String readWSAndExpComments(int startPos, String separator, boolean separatorOptional) throws ConverterException { int pos = getPositionAfterWSAndExpComments(startPos); - if (pos == src.length() || src.charAt(pos) != separator) { + if (pos == src.length() || !src.startsWith(separator, pos)) { // No separator return separatorOptional ? src.substring(startPos, pos) : ""; } - pos++; // Skip separator + pos += separator.length(); pos = getPositionAfterWSAndExpComments(pos); return src.substring(startPos, pos); } + private int printWSAndExpComments(int pos) throws ConverterException { + String sep = readWSAndExpComments(pos); + printWithConvertedExpComments(sep); + pos += sep.length(); + return pos; + } + + private int printWSAndExpComments(int pos, String separator, boolean sepOptional) throws + ConverterException { + String sep = readWSAndExpComments(pos, separator, sepOptional); + printWithConvertedExpComments(sep); + pos += sep.length(); + return pos; + } + private int getPositionAfterIdentifier(int startPos) throws ConverterException { int pos = startPos; scanUntilIdentifierEnd: while (pos < src.length()) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1f086e3f/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java ---------------------------------------------------------------------- diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java index 9166e35..0f74125 100644 --- a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java +++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java @@ -70,19 +70,19 @@ public class FM2ToFM3ConverterTest extends ConverterTest { assertConvertedSame("${f([ <#-- C --> ])}"); assertConvertedSame("${f([1])}"); assertConvertedSame("${f([1, [x,y], 3])}"); - assertConvertedSame("${f([<#-- C1 --> 1, <#-- C2 --> 2, <#-- C3 --> 3 <#-- C4 -->])}"); + assertConvertedSame("${f([<#--1--> 1, <#--2--> 2, <#--3--> 3 <#--4-->])}"); assertConvertedSame("${f({})}"); assertConvertedSame("${f({k: v})}"); assertConvertedSame("${f({k1: v1, k2: v2, 'k3': 33})}"); - assertConvertedSame("${f({ <#-- C1 --> k1 <#-- C1 --> : <#-- C1 --> v1 <#-- C1 -->,k2:v2 <#-- C1 -->})}"); + assertConvertedSame("${f({ <#--1--> k1 <#--2--> : <#--3--> v1 <#--4-->,k2:v2 <#--5-->})}"); assertConvertedSame("${f(1 .. 9)}"); assertConvertedSame("${f(1 ..* 9)}"); assertConvertedSame("${f(1 ..! 9)}"); assertConvertedSame("${f(1 ..< 9)}"); assertConvertedSame("${f(1 ..)}"); - assertConvertedSame("${f(1<#-- C1 -->..\t<#-- C2 -->9)}"); + assertConvertedSame("${f(1<#--1-->..\t<#--2-->9)}"); } @Test @@ -93,24 +93,24 @@ public class FM2ToFM3ConverterTest extends ConverterTest { assertConvertedSame("${f()}"); assertConvertedSame("${f(1)}"); assertConvertedSame("${f(1, 2)}"); - assertConvertedSame("${f<#-- C1 -->(<#-- C2 --> 1, 2 ,<#-- C3 --> 3,<#-- C4 -->4 <#-- C5 -->)}"); + assertConvertedSame("${f<#--1-->(<#--2--> 1, 2 ,<#--3--> 3,<#--4-->4 <#--5-->)}"); assertConvertedSame("${m[key]}"); assertConvertedSame("${m['key']}"); - assertConvertedSame("${m <#-- C1 --> [ <#-- C2 --> key <#-- C3 --> ]}"); + assertConvertedSame("${m <#--1--> [ <#--2--> key <#--3--> ]}"); assertConvertedSame("${seq\\-foo[1]}"); assertConvertedSame("${m.key}"); - assertConvertedSame("${m <#-- C1 --> . <#-- C3 --> key}"); + assertConvertedSame("${m <#--1--> . <#--3--> key}"); assertConvertedSame("${.outputFormat}"); assertConvertedSame("${. <#-- C --> outputFormat}"); assertConverted("${.outputFormat}","${.output_format}"); assertConvertedSame("${a < b}${a <= b}${(a > b)}${(a >= b)}${a == b}${a != b}"); - assertConvertedSame("${a<#-- C1 --><<#-- C2 -->b}${a<#-- C3 --><=<#-- C4 -->b}" - + "${(a<#-- C7 -->><#-- C8 -->b)}${(a<#-- C9 -->>=<#-- CA -->b)}" - + "${a<#-- CB -->==<#-- CC -->b}${a<#-- CD -->!=<#-- CE -->b}"); + assertConvertedSame("${a<#--1--><<#--2-->b}${a<#--3--><=<#--4-->b}" + + "${(a<#--7-->><#--8-->b)}${(a<#--9-->>=<#--A-->b)}" + + "${a<#--B-->==<#--C-->b}${a<#--D-->!=<#--E-->b}"); // "Same" for now, will be different later. assertConvertedSame("${a = b}${a == b}"); assertConvertedSame("${a < b}${a lt b}${a \\lt b}"); @@ -120,19 +120,19 @@ public class FM2ToFM3ConverterTest extends ConverterTest { // [FM3] Add \and and && tests when 2.3.27 is released assertConvertedSame("${a && b}${a & b}${a || b}${a | b}"); - assertConvertedSame("${a<#-- C1 -->&&<#-- C2 -->b}${a<#-- C3 -->&<#-- C4 -->b}" - + "${a<#-- C5 -->||<#-- C6 -->b}${a<#-- C7 -->|<#-- C8 -->b}"); + assertConvertedSame("${a<#--1-->&&<#--2-->b}${a<#--3-->&<#--4-->b}" + + "${a<#--5-->||<#--6-->b}${a<#--7-->|<#--8-->b}"); - assertConvertedSame("${!a}${! foo}${! <#-- C1 --> bar}${!!c}"); + assertConvertedSame("${!a}${! foo}${! <#--1--> bar}${!!c}"); assertConvertedSame("${a!} ${a!0}"); - assertConvertedSame("${a <#-- C1 --> !} ${a <#-- C2 --> ! <#-- C3 --> 0}"); + assertConvertedSame("${a <#--1--> !} ${a <#--2--> ! <#--3--> 0}"); assertConvertedSame("${a!b.c(x!0, y!0)}"); assertConvertedSame("${(a.b)!x}"); // [FM3] Will be: a!(x+1) assertConvertedSame("${a!x+1}"); - assertConvertedSame("${a??} ${a <#-- C1 --> ??}"); + assertConvertedSame("${a??} ${a <#--1--> ??}"); } @Test @@ -166,25 +166,43 @@ public class FM2ToFM3ConverterTest extends ConverterTest { assertConvertedSame("<#if foo >1<#elseIf bar >2<#else >3</#if >"); assertConvertedSame("<#macro m>body</#macro>"); - assertConvertedSame("<#macro <#-- C1 --> m <#-- C2 -->></#macro >"); + assertConvertedSame("<#macro <#--1--> m <#--2-->></#macro >"); assertConvertedSame("<#macro m()></#macro>"); - assertConvertedSame("<#macro m <#-- C1 --> ( <#-- C2 --> ) <#-- C3 --> ></#macro>"); + assertConvertedSame("<#macro m <#--1--> ( <#--2--> ) <#--3--> ></#macro>"); assertConvertedSame("<#macro m p1></#macro>"); assertConvertedSame("<#macro m(p1)></#macro>"); assertConvertedSame("<#macro m p1 p2 p3></#macro>"); - assertConvertedSame("<#macro m p1 <#-- C1 --> p2 <#-- C2 --> p3 <#-- C3 -->></#macro>"); - assertConvertedSame("<#macro m(p1<#-- C1 -->,<#-- C2 --> p2<#-- C3 -->,<#-- C4 -->" - + " p5<#-- C5 -->)<#-- C6 -->></#macro>"); + assertConvertedSame("<#macro m p1 <#--1--> p2 <#--2--> p3 <#--3-->></#macro>"); + assertConvertedSame("<#macro m(p1<#--1-->,<#--2--> p2<#--3-->,<#--4-->" + + " p5<#--5-->)<#--6-->></#macro>"); assertConvertedSame("<#macro m p1=11 p2=foo p3=a+b></#macro>"); assertConvertedSame("<#macro m(p1=11, p2=foo, p3=a+b)></#macro>"); - assertConvertedSame("<#macro m p1<#-- C1 -->=<#-- C2 -->11<#-- C3 -->,<#-- C4 -->p2=22></#macro>"); + assertConvertedSame("<#macro m p1<#--1-->=<#--2-->11<#--3-->,<#--4-->p2=22></#macro>"); assertConvertedSame("<#macro m others...></#macro>"); assertConvertedSame("<#macro m p1 others...></#macro>"); assertConvertedSame("<#macro m p1 p2=22 others...></#macro>"); assertConvertedSame("<#macro m(others...)></#macro>"); - assertConvertedSame("<#macro m(others <#-- C1 --> ... <#-- C2 --> )></#macro>"); + assertConvertedSame("<#macro m(others <#--1--> ... <#--2--> )></#macro>"); assertConvertedSame("<#function m x y>foo</#function>"); assertConvertedSame("<#macro m\\-1 p\\-1></#macro>"); + + assertConvertedSame("<#assign x = 1>"); + assertConvertedSame("<#global x = 1>"); + assertConvertedSame("<#macro m><#local x = 1></#macro>"); + assertConvertedSame("<#assign x = 1 in someNs>"); + assertConvertedSame("<#assign <#--1--> x <#--2--> = <#--3--> 1 <#--4--> in <#--5--> someNs <#--6-->>"); + assertConvertedSame("<#assign x=1 y=2,z=3>"); + assertConvertedSame("<#assign x += 1>"); + assertConvertedSame("<#assign x *= 2>"); + assertConvertedSame("<#assign x-->"); + assertConvertedSame("<#global x = 1, y++, z /= 2>"); + assertConvertedSame("<#assign x = 1 y++ z /= 2>"); + assertConvertedSame("<#assign <#--0-->x = 1<#--1-->,<#--2-->y++<#--3-->z/=2<#--4-->>"); + + assertConvertedSame("<#attempt>a<#recover>r</#attempt>"); + assertConvertedSame("<#attempt >a<#recover >r</#attempt >"); + assertConverted("<#attempt>a<#recover>r</#attempt>", "<#attempt>a<#recover>r</#recover>"); + assertConverted("<#attempt >a<#recover >r</#attempt >", "<#attempt >a<#recover >r</#recover >"); } @Test @@ -199,14 +217,14 @@ public class FM2ToFM3ConverterTest extends ConverterTest { assertConvertedSame("<@foo x\\-y=1 />"); assertConvertedSame("<@foo\n\tx = 1\n\ty = 2\n/>"); assertConvertedSame("<@foo 1 2 />"); - assertConvertedSame("<@foo <#-- C1 --> 1 <#-- C2 --> 2 <#-- C3 --> />"); + assertConvertedSame("<@foo <#--1--> 1 <#--2--> 2 <#--3--> />"); assertConvertedSame("<@foo 1, 2 />"); - assertConvertedSame("<@foo <#-- C1 --> 1 <#-- C2 -->, <#-- C3 --> 2 <#-- C4 --> />"); + assertConvertedSame("<@foo <#--1--> 1 <#--2-->, <#--3--> 2 <#--4--> />"); assertConvertedSame("<@foo x=1; i, j></@>"); assertConvertedSame("<@foo 1; i, j></@>"); assertConvertedSame("<@foo 1 2; i\\-2, j></@>"); assertConvertedSame("<@foo x=1 y=2; i></@>"); - assertConvertedSame("<@foo x=1 ;\n i <#-- C0 --> , <#-- C1 -->\n\t<!-- C2 --> j <#-- C3 -->\n></@>"); + assertConvertedSame("<@foo x=1 ;\n i <#-- C0 --> , <#--1-->\n\t<!-- C2 --> j <#--3-->\n></@>"); } @Test @@ -219,7 +237,7 @@ public class FM2ToFM3ConverterTest extends ConverterTest { @Test public void testComments() throws IOException, ConverterException { assertConvertedSame("\n<#--\n c\n\t-->\n"); - assertConvertedSame("${1 + <#-- C1 -->\r\n2 +[#-- C2 --]3 +<!--\tC3\t-->4 +[!-- C4 --] 5 + -<!-- -->1}"); + assertConvertedSame("${1 + <#--1-->\r\n2 +[#-- C2 --]3 +<!--\tC3\t-->4 +[!-- C4 --] 5 + -<!-- -->1}"); } @Test