📄 fmparser.jj
字号:
*/
Expression DotVariable(Expression exp) :
{
Token t;
}
{
<DOT>
(
t=<ID> | t=<TIMES> | t=<DOUBLE_STAR>
|
(
t=<LESS_THAN>
| t=<LESS_THAN_EQUALS>
| t=<ESCAPED_GT>
| t=<ESCAPED_GTE>
| t=<FALSE>
| t=<TRUE>
| t=<IN>
| t=<AS>
| t=<USING>
)
{
if (!Character.isLetter(t.image.charAt(0))) {
String msg = getErrorStart(t)
+ "\n" + t.image + " is not a valid identifier.";
throw new ParseException(msg, t.beginLine, t.beginColumn);
}
}
)
{
notListLiteral(exp, "hash");
notStringLiteral(exp, "hash");
notBooleanLiteral(exp, "hash");
Dot dot = new Dot(exp, t.image);
dot.setLocation(template, exp, t);
return dot;
}
}
/**
* production for when the key is specified
* in brackets.
*/
Expression DynamicKey(Expression exp) :
{
Expression arg;
Token t;
}
{
<OPEN_BRACKET>
arg=Expression()
t=<CLOSE_BRACKET>
{
notBooleanLiteral(exp, "list or hash");
notNumberLiteral(exp, "list or hash");
DynamicKeyName dkn = new DynamicKeyName(exp, arg);
dkn.setLocation(template, exp, t);
return dkn;
}
}
/**
* production for an arglist part of a method invocation.
*/
MethodCall MethodArgs(Expression exp) :
{
ArrayList args = new ArrayList();
Token end;
}
{
<OPEN_PAREN>
args=PositionalArgs()
end=<CLOSE_PAREN>
{
args.trimToSize();
MethodCall result = new MethodCall(exp, args);
result.setLocation(template, exp, end);
return result;
}
}
StringLiteral StringLiteral(boolean interpolate) :
{
Token t;
boolean raw = false;
}
{
(
t=<STRING_LITERAL>
|
t=<RAW_STRING> {raw = true;}
)
{
String s = t.image;
// Get rid of the quotes.
s = s.substring(1, s.length() -1);
if (raw) {
s=s.substring(1);
}
else try {
s = StringUtil.FTLStringLiteralDec(s);
} catch (ParseException pe) {
pe.lineNumber = t.beginLine;
pe.columnNumber = t.beginColumn;
throw pe;
}
StringLiteral result = new StringLiteral(s);
result.setLocation(template, t, t);
if (interpolate && !raw) {
result.checkInterpolation();
}
return result;
}
}
Expression BooleanLiteral() :
{
Token t;
Expression result;
}
{
(
t=<FALSE> {result = new BooleanLiteral(false);}
|
t=<TRUE> {result = new BooleanLiteral(true);}
)
{
result.setLocation(template, t, t);
return result;
}
}
HashLiteral HashLiteral() :
{
Token begin, end;
Expression key, value;
ArrayList keys = new ArrayList();
ArrayList values = new ArrayList();
}
{
begin=<OPEN_BRACE>
[
key=Expression()
(<COMMA>|<COLON>)
value=Expression()
{
stringLiteralOnly(key);
keys.add(key);
values.add(value);
}
(
<COMMA>
key=Expression()
(<COMMA>|<COLON>)
value=Expression()
{
stringLiteralOnly(key);
keys.add(key);
values.add(value);
}
)*
]
end=<CLOSE_BRACE>
{
HashLiteral result = new HashLiteral(keys, values);
result.setLocation(template, begin, end);
return result;
}
}
/**
* A production representing the ${...}
* that outputs a variable.
*/
DollarVariable StringOutput() :
{
Expression exp;
Token begin, end;
}
{
begin=<OUTPUT_ESCAPE>
exp=Expression()
{
notHashLiteral(exp, "scalar");
notListLiteral(exp, "scalar");
notBooleanLiteral(exp, "scalar");
}
end=<CLOSE_BRACE>
{
DollarVariable result = new DollarVariable(exp, escapedExpression(exp));
result.setLocation(template, begin, end);
return result;
}
}
NumericalOutput NumericalOutput() :
{
Expression exp;
Token fmt = null, begin, end;
}
{
begin=<NUMERICAL_ESCAPE>
exp=Expression() {numberLiteralOnly(exp);}
[
<SEMICOLON>
fmt=<ID>
]
end=<CLOSE_BRACE>
{
NumericalOutput result;
if (fmt != null) {
int minFrac = -1; // -1 indicates that the value has not been set
int maxFrac = -1;
StringTokenizer st = new StringTokenizer(fmt.image, "mM", true);
char type = '-';
while (st.hasMoreTokens()) {
String token = st.nextToken();
try {
if (type != '-') {
switch (type) {
case 'm':
if (minFrac != -1) throw new ParseException("invalid formatting string", fmt.beginLine, fmt.beginColumn);
minFrac = Integer.parseInt(token);
break;
case 'M':
if (maxFrac != -1) throw new ParseException("invalid formatting string", fmt.beginLine, fmt.beginColumn);
maxFrac = Integer.parseInt(token);
break;
default:
throw new ParseException();
}
type = '-';
} else if (token.equals("m")) {
type = 'm';
} else if (token.equals("M")) {
type = 'M';
} else {
throw new ParseException();
}
}
catch (ParseException e) {
String msg = getErrorStart(fmt)
+ "\nInvalid format specifier "
+ fmt.image;
throw new ParseException(msg, fmt.beginLine, fmt.beginColumn);
}
catch (NumberFormatException e) {
String msg = getErrorStart(fmt)
+ "\nInvalid number in the format specifier "
+ fmt.image;
throw new ParseException(msg, fmt.beginLine, fmt.beginColumn);
}
}
if (maxFrac == -1) {
if (minFrac == -1) {
String msg = getErrorStart(fmt)
+ "\nInvalid format specification, at least one of m and M must be specified!";
throw new ParseException(msg, fmt.beginLine, fmt.beginColumn);
}
maxFrac = minFrac;
} else if (minFrac == -1) {
minFrac = 0;
}
if (minFrac > maxFrac) {
String msg = getErrorStart(fmt)
+ "\nInvalid format specification, min cannot be greater than max!";
throw new ParseException(msg, fmt.beginLine, fmt.beginColumn);
}
if (minFrac > 50 || maxFrac > 50) {// sanity check
String msg = getErrorStart(fmt)
+ "\nCannot specify more than 50 fraction digits";
throw new ParseException(msg, fmt.beginLine, fmt.beginColumn);
}
result = new NumericalOutput(exp, minFrac, maxFrac);
} else { // if format != null
result = new NumericalOutput(exp);
}
result.setLocation(template, begin, end);
return result;
}
}
TemplateElement If() :
{
Token start, end, t;
Expression condition;
TemplateElement block;
IfBlock ifBlock;
ConditionalBlock cblock;
}
{
start=<IF>
condition=Expression()
<DIRECTIVE_END>
block=OptionalBlock()
{
cblock = new ConditionalBlock(condition, block, true);
cblock.setLocation(template, start, block);
ifBlock = new IfBlock(cblock);
}
(
t=<ELSE_IF>
condition=Expression()
LooseDirectiveEnd()
block=OptionalBlock()
{
cblock = new ConditionalBlock(condition, block, false);
cblock.setLocation(template, t, block);
ifBlock.addBlock(cblock);
}
)*
[
t=<ELSE>
block=OptionalBlock()
{
cblock = new ConditionalBlock(null, block, false);
cblock.setLocation(template, t, block);
ifBlock.addBlock(cblock);
}
]
end=<END_IF>
{
ifBlock.setLocation(template, start, end);
return ifBlock;
}
}
AttemptBlock Attempt() :
{
Token start, end;
TemplateElement block, recoveryBlock;
}
{
start=<ATTEMPT>
block=OptionalBlock()
recoveryBlock=Recover()
(
end=<END_RECOVER>
|
end=<END_ATTEMPT>
)
{
AttemptBlock result = new AttemptBlock(block, recoveryBlock);
result.setLocation(template, start, end);
return result;
}
}
RecoveryBlock Recover() :
{
Token start;
TemplateElement block;
}
{
start=<RECOVER>
block=OptionalBlock()
{
RecoveryBlock result = new RecoveryBlock(block);
result.setLocation(template, start, block);
return result;
}
}
IteratorBlock List() :
{
Expression exp;
Token index, start, end;
TemplateElement block;
}
{
start=<LIST> {++loopNesting;}
exp=Expression()
<AS>
index=<ID>
<DIRECTIVE_END>
block=OptionalBlock()
end=<END_LIST>
{
--loopNesting;
IteratorBlock result = new IteratorBlock(exp,
index.image,
block,
false);
result.setLocation(template, start, end);
return result;
}
}
IteratorBlock ForEach() :
{
Expression exp;
Token index, start, end;
TemplateElement block;
}
{
start=<FOREACH> {++loopNesting;}
index=<ID>
<IN>
exp=Expression()
<DIRECTIVE_END>
block=OptionalBlock()
end=<END_FOREACH>
{
--loopNesting;
IteratorBlock result = new IteratorBlock(exp,
index.image,
block,
true);
result.setLocation(template, start, end);
return result;
}
}
VisitNode Visit() :
{
Token start, end;
Expression targetNode, namespaces=null;
}
{
start=<VISIT>
targetNode=Expression()
[
<USING>
namespaces=Expression()
]
end=LooseDirectiveEnd()
{
VisitNode result = new VisitNode(targetNode, namespaces);
result.setLocation(template, start, end);
return result;
}
}
RecurseNode Recurse() :
{
Token start, end = null;
Expression node=null, namespaces=null;
}
{
(
start=<SIMPLE_RECURSE>
|
(
start=<RECURSE>
[
node=Expression()
]
[
<USING>
namespaces=Expression()
]
end=LooseDirectiveEnd()
)
)
{
if (end == null) end = start;
RecurseNode result = new RecurseNode(node, namespaces);
result.setLocation(template, start, end);
return result;
}
}
FallbackInstruction FallBack() :
{
Token tok;
}
{
tok=<FALLBACK>
{
if (!inMacro) {
throw new ParseException(getErrorStart(tok)
+ "\nCannot fall back "
+ " outside a macro.",
tok.beginLine, tok.beginColumn);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -