diff --git a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 index 0e4c672..3b72c01 100644 --- a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 +++ b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 @@ -505,8 +505,11 @@ cfmlExpression // FINAL is Lucee's immutable local; it takes the same shape as VAR and may combine // with it, as in `final var x = 1;`. -localAssignmentExpression - : (VAR | FINAL VAR? | VAR FINAL) left=startExpression ( (EQUALSOP otherIdentifiers)* EQUALSOP right=startExpression )? //-> ^( VARLOCAL identifier ( EQUALSOP baseExpression )? ) +// STATIC sits here alongside FINAL because `static x = 1;` is a declaration, not a function. +// Without it, STATIC is only reachable as a functionModifier, so functionDeclaration wins +// prediction and then finds no FUNCTION token -- see #63. +localAssignmentExpression + : (VAR | (FINAL | STATIC) VAR? | VAR (FINAL | STATIC)) left=startExpression ( (EQUALSOP otherIdentifiers)* EQUALSOP right=startExpression )? //-> ^( VARLOCAL identifier ( EQUALSOP baseExpression )? ) ; otherIdentifiers: diff --git a/cfml.parsing/src/main/java/cfml/parsing/cfscript/CFVarDeclExpression.java b/cfml.parsing/src/main/java/cfml/parsing/cfscript/CFVarDeclExpression.java index 658ec6b..254aee1 100644 --- a/cfml.parsing/src/main/java/cfml/parsing/cfscript/CFVarDeclExpression.java +++ b/cfml.parsing/src/main/java/cfml/parsing/cfscript/CFVarDeclExpression.java @@ -16,6 +16,8 @@ public class CFVarDeclExpression extends CFExpression { private CFExpression init; // null if none /** True for Lucee's `final x = 1;`, which is a different declaration from `var x = 1;`. */ private boolean finalDecl = false; + /** True for `static x = 1;`, a component-level static member rather than a local. */ + private boolean staticDecl = false; List otherVars = new ArrayList(); List otherIds = new ArrayList(); @@ -42,7 +44,7 @@ public CFVarDeclExpression(Token _t, CFExpression _var, CFExpression _init) { @Override public String Decompile(int indent) { StringBuilder s = new StringBuilder(Indent(indent)); - s.append(finalDecl ? "final " : "var "); + s.append(staticDecl ? "static " : finalDecl ? "final " : "var "); s.append(var.Decompile(0)); for (CFIdentifier id : otherVars) { s.append(" = var "); @@ -66,6 +68,14 @@ public boolean isFinal() { public void setFinal(boolean b) { finalDecl = b; } + + public boolean isStatic() { + return staticDecl; + } + + public void setStatic(boolean b) { + staticDecl = b; + } public CFExpression getInit() { return init; diff --git a/cfml.parsing/src/main/java/cfml/parsing/cfscript/walker/CFExpressionVisitor.java b/cfml.parsing/src/main/java/cfml/parsing/cfscript/walker/CFExpressionVisitor.java index 835d820..20d9520 100644 --- a/cfml.parsing/src/main/java/cfml/parsing/cfscript/walker/CFExpressionVisitor.java +++ b/cfml.parsing/src/main/java/cfml/parsing/cfscript/walker/CFExpressionVisitor.java @@ -185,6 +185,7 @@ public CFExpression visitLocalAssignmentExpression(LocalAssignmentExpressionCont final CFExpression initExpression = ctx.right == null ? null : visit(ctx.right); CFVarDeclExpression retval = new CFVarDeclExpression(ctx.start, visit(ctx.left), initExpression); retval.setFinal(ctx.FINAL() != null); + retval.setStatic(ctx.STATIC() != null); if (ctx.otherIdentifiers().size() > 0) { for (OtherIdentifiersContext oi : ctx.otherIdentifiers()) { CFIdentifier otherid = (CFIdentifier) visit(oi.identifier()); diff --git a/cfml.parsing/src/test/resources/cfml/tests/components/static_assignment_63.cfc b/cfml.parsing/src/test/resources/cfml/tests/components/static_assignment_63.cfc new file mode 100644 index 0000000..056a4d4 --- /dev/null +++ b/cfml.parsing/src/test/resources/cfml/tests/components/static_assignment_63.cfc @@ -0,0 +1,9 @@ +component { + static myStatic = "v"; + final myConst = "c"; + + function f() { + var local1 = myStatic; + return local1; + } +} diff --git a/cfml.parsing/src/test/resources/cfml/tests/components/static_assignment_63.expected.txt b/cfml.parsing/src/test/resources/cfml/tests/components/static_assignment_63.expected.txt new file mode 100644 index 0000000..d426b7e --- /dev/null +++ b/cfml.parsing/src/test/resources/cfml/tests/components/static_assignment_63.expected.txt @@ -0,0 +1,144 @@ +/*===TOKENS===*/ +COMPONENT +'{' <{> +Hidden:NEWLINE <> +STATIC +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +';' <;> +Hidden:NEWLINE <> +FINAL +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +';' <;> +Hidden:NEWLINE <> +FUNCTION +IDENTIFIER +'(' <(> +')' <)> +'{' <{> +Hidden:NEWLINE <> +VAR +IDENTIFIER +'=' <=> +IDENTIFIER +';' <;> +Hidden:NEWLINE <> +RETURN +IDENTIFIER +';' <;> +Hidden:NEWLINE <> +'}' <}> +Hidden:NEWLINE <> +'}' <}> +Hidden:NEWLINE <> +/*===TREE===*/ +(scriptBlock + (componentDeclaration + component + (componentGuts + { + (element + (statement + (localAssignmentExpression + static + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier myStatic)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart v) ")) + ) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + final + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier myConst)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart c) ")) + ) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (functionDeclaration + function + (identifier f) + ( + parameterList + ) + (compoundStatement + { + (statement + (localAssignmentExpression + var + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier local1)))) + ) + = + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier myStatic)))) + ) + ) + (endOfStatement ;) + ) + (statement + (returnStatement + return + (anExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier local1)))) + ) + ) + ) + (endOfStatement ;) + ) + } + ) + ) + ) + } + ) + ) +) +/*======*/ +/*===DECOMPILE===*/ +component { + { + static myStatic = 'v'; + final myConst = 'c'; + public function f() { + var local1 = myStatic; + return local1; + + }; + + } +} +/*======*/ \ No newline at end of file