diff --git a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 index fc6e813..065f17a 100644 --- a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 +++ b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 @@ -509,13 +509,20 @@ cfmlExpression | importStatement EOF ; -// FINAL is Lucee's immutable local; it takes the same shape as VAR and may combine -// with it, as in `final var x = 1;`. +// FINAL is Lucee's immutable local; it takes the same shape as VAR and may lead it, as in +// `final var x = 1;`. // 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. +// +// The modifier only ever leads. A trailing `VAR (FINAL | STATIC)` alternative used to sit here +// and could not match anything: both words are in the identifier rule, put there by #46 so code +// naming a variable `final` keeps working, so `var final` matches the plain VAR alternative with +// `final` as the identifier before the longer one is considered. That reading is the correct one +// -- tree-sitter-cfml's corpus tests `var final = getValue();` as a local named final -- so the +// alternative was removed rather than made reachable. See #68. localAssignmentExpression - : (VAR | (FINAL | STATIC) VAR? | VAR (FINAL | STATIC)) left=startExpression ( (EQUALSOP otherIdentifiers)* EQUALSOP right=startExpression )? //-> ^( VARLOCAL identifier ( EQUALSOP baseExpression )? ) + : (VAR | (FINAL | STATIC) VAR?) left=startExpression ( (EQUALSOP otherIdentifiers)* EQUALSOP right=startExpression )? //-> ^( VARLOCAL identifier ( EQUALSOP baseExpression )? ) ; otherIdentifiers: diff --git a/cfml.parsing/src/test/resources/cfml/tests/expressions/var_modifiers_68.cfc b/cfml.parsing/src/test/resources/cfml/tests/expressions/var_modifiers_68.cfc new file mode 100644 index 0000000..efb34da --- /dev/null +++ b/cfml.parsing/src/test/resources/cfml/tests/expressions/var_modifiers_68.cfc @@ -0,0 +1,9 @@ +var plain = 1; +final immutable = 2; +static shared = 3; +final var finalLocal = 4; +static var staticLocal = 5; +final = getValue(); +var final = getValue(); +static = getValue(); +var static = getValue(); diff --git a/cfml.parsing/src/test/resources/cfml/tests/expressions/var_modifiers_68.expected.txt b/cfml.parsing/src/test/resources/cfml/tests/expressions/var_modifiers_68.expected.txt new file mode 100644 index 0000000..15ad922 --- /dev/null +++ b/cfml.parsing/src/test/resources/cfml/tests/expressions/var_modifiers_68.expected.txt @@ -0,0 +1,232 @@ +/*===TOKENS===*/ +VAR +IDENTIFIER +'=' <=> +INTEGER_LITERAL <1> +';' <;> +Hidden:NEWLINE <> +FINAL +IDENTIFIER +'=' <=> +INTEGER_LITERAL <2> +';' <;> +Hidden:NEWLINE <> +STATIC +IDENTIFIER +'=' <=> +INTEGER_LITERAL <3> +';' <;> +Hidden:NEWLINE <> +FINAL +VAR +IDENTIFIER +'=' <=> +INTEGER_LITERAL <4> +';' <;> +Hidden:NEWLINE <> +STATIC +VAR +IDENTIFIER +'=' <=> +INTEGER_LITERAL <5> +';' <;> +Hidden:NEWLINE <> +FINAL +'=' <=> +IDENTIFIER +'(' <(> +')' <)> +';' <;> +Hidden:NEWLINE <> +VAR +FINAL +'=' <=> +IDENTIFIER +'(' <(> +')' <)> +';' <;> +Hidden:NEWLINE <> +STATIC +'=' <=> +IDENTIFIER +'(' <(> +')' <)> +';' <;> +Hidden:NEWLINE <> +VAR +STATIC +'=' <=> +IDENTIFIER +'(' <(> +')' <)> +';' <;> +Hidden:NEWLINE <> +/*===TREE===*/ +(scriptBlock + (element + (statement + (localAssignmentExpression + var + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier plain)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 1)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + final + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier immutable)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 2)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + static + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier shared)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 3)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + final + var + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier finalLocal)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 4)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + static + var + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier staticLocal)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 5)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier final)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (memberExpression (functionCall (identifier getValue) ( argumentList ))) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + var + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier final)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (memberExpression (functionCall (identifier getValue) ( argumentList ))) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier static)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (memberExpression (functionCall (identifier getValue) ( argumentList ))) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (localAssignmentExpression + var + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier static)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (memberExpression (functionCall (identifier getValue) ( argumentList ))) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) +) +/*======*/ +/*===DECOMPILE===*/ +{ + var plain = 1; + final immutable = 2; + static shared = 3; + final finalLocal = 4; + static staticLocal = 5; +final = getValue(); + var final = getValue(); +static = getValue(); + var static = getValue(); + +} +/*======*/ \ No newline at end of file