Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion cfml.parsing/src/main/antlr4/cfml/CFSCRIPTLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ FINAL: [fF][iI][nN][aA][lL];
ABSTRACT: [aA][Bb][sS][tT][Rr][aA][cC][tT];
STATIC: [sS][tT][aA][tT][iI][cC];
SCRIPTOPEN: '<' [cC] [fF] [sS] [cC] [rR] [iI] [pP] [tT] '>';
// Lucee's template block: ``` drops out of cfscript into template markup, the mirror of
// <cfscript> dropping the other way. The body interpolates #...# like a string does.
OPEN_TEMPLATE: '```' -> pushMode(InTemplate);
SCRIPTCLOSE:'</' [cC] [fF] [sS] [cC] [rR] [iI] [pP] [tT] '>';
// operators
DOT: '.';
Expand Down Expand Up @@ -456,4 +459,17 @@ HASH_SINGLE

mode HashMode;
HashMode_ANY: -> popMode,skip;


mode InTemplate;
CLOSE_TEMPLATE
: '```' -> popMode
;
TEMPLATE_DOUBLEHASH
: '##' -> type(DOUBLEHASH)
;
TEMPLATE_LITERAL
: (~[`#]+ | '`' ~[`] | '``' ~[`])+
;
TEMPLATE_HASH
: '#' -> type(POUND_SIGN),pushMode(HashMode),pushMode(DefaultMode)
;
11 changes: 11 additions & 0 deletions cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ cfscriptBlock
: SCRIPTOPEN scriptBlock SCRIPTCLOSE
;

// The inverse of cfscriptBlock: markup embedded in cfscript, between ``` fences, with
// #...# interpolation as in a string literal.
templateBlock
: OPEN_TEMPLATE (templateLiteralPart | POUND_SIGN anExpression POUND_SIGN)* CLOSE_TEMPLATE
;

templateLiteralPart
: TEMPLATE_LITERAL | DOUBLEHASH
;

componentDeclaration
: componentModifier? COMPONENT componentAttribute* componentGuts //-> ( COMPDECL componentAttribute* componentGuts)
;
Expand Down Expand Up @@ -154,6 +164,7 @@ statement
| startExpression SEMICOLON
| SEMICOLON // empty statement
| functionCall // without semi
| templateBlock
;

endOfStatement
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package cfml.parsing.cfscript.script;

import java.util.ArrayList;
import java.util.List;

import org.antlr.v4.runtime.Token;

import cfml.parsing.cfscript.CFExpression;
import cfml.parsing.util.ArrayBuilder;

/**
* Lucee's template block: markup embedded in cfscript between <code>```</code> fences, the mirror
* of <code>&lt;cfscript&gt;</code> dropping the other way.
*
* The body is markup rather than cfscript, so it is kept as its source text and written back
* unchanged. What a consumer needs from it is the interpolated <code>#...#</code> expressions,
* which are parsed and reachable through {@link #getExpressions()} and {@link
* #decomposeExpression()} -- a variable used only inside a template block is still a use.
*/
public class CFTemplateBlockStatement extends CFParsedStatement {

private static final long serialVersionUID = 1L;

private static final String FENCE = "```";

private String content;
private List<CFExpression> expressions;

public CFTemplateBlockStatement(Token _t, String _content, List<CFExpression> _expressions) {
super(_t);
content = _content;
expressions = _expressions == null ? new ArrayList<CFExpression>() : _expressions;
for (CFExpression expression : expressions) {
expression.setParent(this);
}
}

/** The markup between the fences, exactly as written, interpolations included. */
public String getContent() {
return content;
}

/** The <code>#...#</code> expressions inside the block, in source order. */
public List<CFExpression> getExpressions() {
return expressions;
}

@Override
public String Decompile(int indent) {
return FENCE + content + FENCE;
}

@Override
public List<CFExpression> decomposeExpression() {
return expressions;
}

@Override
public List<CFScriptStatement> decomposeScript() {
return ArrayBuilder.createCFScriptStatement();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
import java.util.Stack;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;

import cfml.CFSCRIPTParser.AbortStatementContext;
import cfml.CFSCRIPTParser.AdminStatementContext;
import cfml.CFSCRIPTParser.AnExpressionContext;
import cfml.CFSCRIPTParser.AnonymousFunctionDeclarationContext;
import cfml.CFSCRIPTParser.AssignmentExpressionContext;
import cfml.CFSCRIPTParser.BaseExpressionContext;
Expand Down Expand Up @@ -52,6 +55,7 @@
import cfml.CFSCRIPTParser.StartExpressionContext;
import cfml.CFSCRIPTParser.StatementContext;
import cfml.CFSCRIPTParser.SwitchStatementContext;
import cfml.CFSCRIPTParser.TemplateBlockContext;
import cfml.CFSCRIPTParser.TagFunctionStatementContext;
import cfml.CFSCRIPTParser.TagStatementContext;
import cfml.CFSCRIPTParser.TagThrowStatementContext;
Expand Down Expand Up @@ -95,6 +99,7 @@
import cfml.parsing.cfscript.script.CFScriptStatement;
import cfml.parsing.cfscript.script.CFSwitchStatement;
import cfml.parsing.cfscript.script.CFTagThrowStatement;
import cfml.parsing.cfscript.script.CFTemplateBlockStatement;
import cfml.parsing.cfscript.script.CFThreadStatement;
import cfml.parsing.cfscript.script.CFThrowStatement;
import cfml.parsing.cfscript.script.CFTransactionStatement;
Expand Down Expand Up @@ -585,6 +590,25 @@ public CFScriptStatement visitExitStatement(ExitStatementContext ctx) {
return exitStatement;
}

@Override
public CFScriptStatement visitTemplateBlock(TemplateBlockContext ctx) {
// The body is markup, not cfscript, so it is kept verbatim rather than modelled. Only the
// interpolations are parsed -- without a visitor here the block vanished from the tree and
// its #...# expressions leaked out as bare statements.
List<CFExpression> expressions = new ArrayList<CFExpression>();
for (AnExpressionContext interpolation : ctx.anExpression()) {
CFExpression expression = cfExpressionVisitor.visit(interpolation);
if (expression != null) {
expressions.add(expression);
}
}
Token open = ctx.OPEN_TEMPLATE().getSymbol();
Token close = ctx.CLOSE_TEMPLATE().getSymbol();
String content = open.getInputStream().getText(
Interval.of(open.getStopIndex() + 1, close.getStartIndex() - 1));
return new CFTemplateBlockStatement(open, content, expressions);
}

@Override
public CFScriptStatement visitParamStatement(ParamStatementContext ctx) {
// System.out.println("visitParamStatement");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name = "World";
user = { age: 30 };
```
<h1>Hello #name#</h1>
<p>You are #user.age# years old</p>
```
after = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*===TOKENS===*/
IDENTIFIER <name>
'=' <=>
OPEN_STRING <">
STRING_LITERAL <World>
CLOSE_STRING <">
';' <;>
Hidden:NEWLINE <>
IDENTIFIER <user>
'=' <=>
'{' <{>
IDENTIFIER <age>
':' <:>
INTEGER_LITERAL <30>
'}' <}>
';' <;>
Hidden:NEWLINE <>
OPEN_TEMPLATE <```>
TEMPLATE_LITERAL <<h1>Hello>
'#' <#>
IDENTIFIER <name>
'#' <#>
TEMPLATE_LITERAL <</h1>
<p>You are>
'#' <#>
IDENTIFIER <user>
'.' <.>
IDENTIFIER <age>
'#' <#>
TEMPLATE_LITERAL <years old</p>>
CLOSE_TEMPLATE <```>
Hidden:NEWLINE <>
IDENTIFIER <after>
'=' <=>
INTEGER_LITERAL <1>
';' <;>
Hidden:NEWLINE <>
/*===TREE===*/
(scriptBlock
(element
(statement
(assignmentExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier name))))
)
=
(startExpression
(baseExpression
(unaryExpression
(primaryExpression
(literalExpression (stringLiteral " (stringLiteralPart World) "))
)
)
)
)
)
(endOfStatement ;)
)
)
(element
(statement
(assignmentExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier user))))
)
=
(startExpression
(baseExpression
(unaryExpression
(primaryExpression
(implicitStruct
{
(implicitStructElements
(implicitStructExpression
(implicitStructKeyExpression (multipartIdentifier (identifier age)))
:
(baseExpression (unaryExpression (primaryExpression (literalExpression 30))))
)
)
}
)
)
)
)
)
)
(endOfStatement ;)
)
)
(element
(statement
(templateBlock
```
(templateLiteralPart '\n<h1>Hello ')
#
(anExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier name))))
)
)
#
(templateLiteralPart '</h1>\n<p>You are ')
#
(anExpression
(startExpression
(baseExpression
(unaryExpression (memberExpression (identifier user) . (identifier age)))
)
)
)
#
(templateLiteralPart ' years old</p>\n')
```
)
)
)
(element
(statement
(assignmentExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier after))))
)
=
(startExpression
(baseExpression (unaryExpression (primaryExpression (literalExpression 1))))
)
)
(endOfStatement ;)
)
)
)
/*======*/
/*===DECOMPILE===*/
{
name = 'World';
user = {age:30};
```
<h1>Hello #name#</h1>
<p>You are #user.age# years old</p>
```;
after = 1;

}
/*======*/
Loading