Skip to content

Commit

Permalink
Parser support for null coalescing, generics defaults, and static loc…
Browse files Browse the repository at this point in the history
…al vars
  • Loading branch information
m0rkeulv committed May 7, 2023
1 parent 5b0fe10 commit dbad976
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ public void onSkip(IElementType type, int i, int i1) {
marker_.collapse(operator);
return true;
}
private static boolean parseOperatorNotFollowedBy(PsiBuilder builder_, IElementType operator, IElementType token) {
final PsiBuilder.Marker marker_ = builder_.mark();

IElementType fistElement = builder_.lookAhead(0);
IElementType secondElement = builder_.lookAhead(1);
if (fistElement == operator && secondElement != token) {
if (consumeTokenFast(builder_, operator)) {
marker_.collapse(operator);
return true;
}

}

marker_.rollbackTo();
builder_.setWhitespaceSkippedCallback(null);
return false;
}

public static boolean shiftRight(PsiBuilder builder_, int level_) {
return parseOperator(builder_, OSHIFT_RIGHT, OGREATER, OGREATER);
Expand All @@ -73,6 +90,10 @@ public static boolean gtEq(PsiBuilder builder_, int level_) {
return parseOperator(builder_, OGREATER_OR_EQUAL, OGREATER, OASSIGN);
}

public static boolean ternary(PsiBuilder builder_, int level_) {
return parseOperatorNotFollowedBy(builder_, OQUEST, ODOT);
}


/**
* Make a semi-colon optional in the case that it's preceded by a block statement.
Expand Down
42 changes: 30 additions & 12 deletions src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,15 @@ bitOperation ::= '|' | '&' | '^' { extends=operator }
private additiveOperator ::= '+' | '-' { extends=operator }
private assignableOperator ::= '++' | '--' { extends=operator }
private colonOperator ::= ':' { extends=operator }
isOperator ::= 'is' { extends=operator }
isOperator ::= 'is' { extends=operator }
private iteratorOperator ::= '...' { extends=operator }
private logicAndOperator ::= '&&' { extends=operator }
private logicOrOperator ::= '||' { extends=operator }
private moduloOperator ::= '%' { extends=operator }
private multiplicativeOperator ::= '*' | '/' { extends=operator }
private prefixOperator ::= '-' | '!' | '~' { extends=operator }
private questionOperator ::= '?' { extends=operator }
private coalescingOperator ::= '??' { extends=operator }
private questionOperator ::= <<ternary>> { extends=operator }
private suffixOperator ::= '!' { extends=operator }

// KFROM and KTO are only keywords for abstracts and can be used as identifiers elsewhere in the code (KNEVER is only used for getters/setters)
Expand All @@ -238,12 +239,21 @@ thisExpression ::= 'this'

superExpression ::= 'super'
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}
//Haxe 4.3+
abstractExpression ::= 'abstract'
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}


packageStatement ::= 'package' simpleQualifiedReferenceExpression? ';'
{pin=1 implements="com.intellij.plugins.haxe.lang.psi.HaxePackageStatementPsiMixin" mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePackageStatementPsiMixinImpl"}

private topLevelList ::= topLevel*
private topLevel ::= importStatement | usingStatement | (embeddedMeta* topLevelDeclaration) | moduleDeclaration {recoverWhile="top_level_recover" name="import, using, or top level declaration"}
// TODO: to avoid huge rollbacks we combine module and toplevel declarations
// TODO: the idea is : if the result contains module parts, treat as module if only toplevel treat as toplevel
// TODO: there might be some checks that needs to be done in code (multiple public classes ?)
private moduleOrTopLevelDeclaration ::= ((embeddedMeta* topLevelDeclaration) | moduleBody)+
private topLevel ::= importStatement | usingStatement | moduleOrTopLevelDeclaration {recoverWhile="top_level_recover" name="import, using, or top level declaration"}
//private topLevel ::= importStatement | usingStatement | (embeddedMeta* topLevelDeclaration) | moduleDeclaration {recoverWhile="top_level_recover" name="import, using, or top level declaration"}
private top_level_recover ::= !(ppToken | '@' | 'abstract' | 'class' | 'enum' | 'extern' | 'import' | 'using' | 'interface' | 'private' | 'typedef' | 'final')

// These are top-level parsing entry points for metadata.
Expand All @@ -270,7 +280,6 @@ usingStatement ::= 'using' simpleQualifiedReferenceExpression ';'
implements="com.intellij.plugins.haxe.lang.psi.HaxeUsingStatementPsiMixin"
mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeUsingStatementPsiMixinImpl"
}

private topLevelDeclaration ::= classDeclaration
| interfaceDeclaration
| externClassDeclaration
Expand Down Expand Up @@ -311,7 +320,8 @@ private extern_class_body_part_recover ::= !(pptoken | metaKeyWord | 'dynamic' |
externInterfaceDeclaration ::= externAndMaybePrivate? 'interface' componentName genericParam? inheritList? interfaceBody
{pin=3 mixin="com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxePsiClass" implements="com.intellij.plugins.haxe.lang.psi.HaxeClass"}

moduleDeclaration ::= moduleBody{pin=1}
//see moduleOrTopLevelDeclaration
//moduleDeclaration ::= moduleBody{pin=1}

moduleBody ::= moduleBodyPart+ {pin=1}
private moduleTypes ::= (privateKeyWord? (classDeclaration | enumDeclaration | typedefDeclaration | abstractClassDeclaration))
Expand Down Expand Up @@ -367,9 +377,9 @@ fieldDeclaration ::= fieldModifier* mutabilityModifier componentName propertyDec
{pin=3 mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePsiFieldImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxePsiField"}
optionalFieldDeclaration ::= fieldModifier* mutabilityModifier '?' componentName propertyDeclaration? typeTag? varInit? <<semicolonUnlessPrecededByStatement>>
{pin=3 implements=fieldDeclaration mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePsiFieldImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxePsiField"}

localVarDeclarationList ::= mutabilityModifier localVarDeclaration (',' localVarDeclaration)* <<semicolonUnlessPrecededByStatement>>{pin=2}
localVarDeclaration ::= componentName typeTag? varInit?
//NOTE: static local variable is a haxe 4.3+ feature
localVarDeclarationList ::='static'? mutabilityModifier localVarDeclaration (',' localVarDeclaration)* <<semicolonUnlessPrecededByStatement>>{pin=3}
localVarDeclaration ::= 'static'? componentName typeTag? varInit?
{recoverWhile="local_var_declaration_part_recover" mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePsiFieldImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxePsiField"}
private localVarDeclarationWithInit ::= componentName typeTag? varInit {extends=localVarDeclaration}
private local_var_declaration_part_recover ::= !('!' | ppToken | '(' | ')' | '++' | ',' | '-' | '--' | ';' | '[' | 'break' | 'case' | 'cast' | 'continue' | 'default' | 'do' | 'else' | 'false' | 'final' | 'for' | 'function' | 'if' | 'new' | 'null' | 'return' | 'super' | 'switch' | 'this' | 'throw' | 'true' | 'try' | 'untyped' | 'var' | 'while' | '{' | '}' | '~' | ID | OPEN_QUOTE | LITFLOAT | LITHEX | LITINT | LITOCT | REG_EXP)
Expand Down Expand Up @@ -482,9 +492,13 @@ typeList ::= typeListPart (',' typeListPart)*
haxe4typeList ::= typeListPart ('&' typeListPart)+
{extends=typeList}

//TODO mlo: separate "type-generics" and "method-generics" as they have different rules
//typeGenericParam ::= '<' defaultGenericListPart (',' defaultGenericListPart)* '>'
//methodGenericParam ::= '<' genericListPart (',' genericListPart)* '>'
genericParam ::= '<' genericListPart (',' genericListPart)* '>'
genericListPart ::= regularGenericListPart | constGenericListPart
genericListPart ::= defaultGenericListPart | regularGenericListPart | constGenericListPart
{mixin="com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxeNamedComponent" implements="com.intellij.plugins.haxe.lang.psi.HaxeComponent"}
private defaultGenericListPart ::= componentName '=' typeWrapper
private regularGenericListPart ::= componentName (':' ('(' (haxe4typeList | typeList) ')' | haxe4typeList | typeListPart ))? {pin=2}
// constGenericListPart is only available when the macroMember is '@:const' (constMeta). It's only useful in macros.
// We can't use constMeta here until the lexer returns a string for macros instead of META_ID.
Expand Down Expand Up @@ -647,6 +661,7 @@ private expression_recover ::= !('!' | '!=' | '%' | '%=' | '&&' | '&' | '&=' | '
// The lookahead predicate on valueExpression is required for suffix expressions to be parsed properly,
// even though the suffix expressions appear before the value.
expression ::= assignExpression
| coalescingExpression
| ternaryExpression
| logicOrExpression
| logicAndExpression
Expand All @@ -666,6 +681,7 @@ expression ::= assignExpression
assignExpression ::= expression assignOperation expression { rightAssociative=true extends=binaryExpression}
isTypeExpression ::= expression isOperator (typeWrapper | literalExpression) { methods=[ leftExpression="/expression[0]" operator="isOperator"] }
iteratorExpression ::= expression iteratorOperator expression { extends=binaryExpression }
coalescingExpression ::= expression coalescingOperator expression { extends=binaryExpression }
ternaryExpression ::= expression questionOperator expression colonOperator expression
{ rightAssociative=true
methods=[ guardExpression="/expression[0]"
Expand Down Expand Up @@ -695,7 +711,7 @@ fake binaryExpression ::= expression* operator+ {
fake unaryExpression ::= expression* operator+ {
methods=[expression="/expression[0]" operator="/operator[0]"]
}
private assignableValueExpression ::= referenceExpression (qualifiedReferenceExpression)* arrayAccessExpression*
private assignableValueExpression ::= referenceExpression (qualifiedNullSafeReferenceExpression)* arrayAccessExpression*


// Be careful when changing the ordering of the 'or' clauses here. It matters!!
Expand All @@ -722,6 +738,7 @@ private value ::= ('untyped' expression)
| referenceExpression
| thisExpression
| superExpression
| abstractExpression
{pin=1}

literalExpression ::= LITINT | LITHEX | LITOCT | LITFLOAT
Expand Down Expand Up @@ -759,10 +776,10 @@ typeCheckExpr ::= expression typeCheckOperator typeWrapper
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeClassReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}

private newExpressionOrCall ::= newExpression qualifiedReferenceTail?
private qualifiedReferenceTail ::= qualifiedReferenceExpression (callExpression | arrayAccessExpression | qualifiedReferenceExpression)*
private qualifiedReferenceTail ::= qualifiedNullSafeReferenceExpression (callExpression | arrayAccessExpression | qualifiedReferenceExpression)*

private callFunctionLiteral ::= functionLiteral callExpression
private callOrArrayAccess ::= (referenceExpression | thisExpression | superExpression | parenthesizedExpression) (callExpression | arrayAccessExpression | qualifiedReferenceExpression)*
private callOrArrayAccess ::= (referenceExpression | thisExpression | superExpression | abstractExpression | parenthesizedExpression) (callExpression | arrayAccessExpression | qualifiedNullSafeReferenceExpression)*
private immediateArrayAccess ::= arrayLiteral arrayAccessExpression

left callExpression ::= '(' expressionList? ')'
Expand All @@ -772,6 +789,7 @@ left arrayAccessExpression ::= '[' expression ']'

referenceExpression ::= identifier
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}
left qualifiedNullSafeReferenceExpression ::= ('?.'|'.') identifier {elementType="referenceExpression" pin=2}
left qualifiedReferenceExpression ::= '.' identifier {elementType="referenceExpression" pin=2}
private simpleQualifiedReferenceExpression ::= referenceExpression qualifiedReferenceExpression * { elementType="referenceExpression"}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
function main() {
trace("Module method );
trace("Module method");
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
Haxe File
MODULE_DECLARATION
MODULE_BODY
MODULE_METHOD_DECLARATION
HaxePsiToken:function('function')
COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('main')
HaxePsiToken:(('(')
PARAMETER_LIST
<empty list>
HaxePsiToken:)(')')
BLOCK_STATEMENT
HaxePsiToken:{('{')
MODULE_BODY
MODULE_METHOD_DECLARATION
HaxePsiToken:function('function')
COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('main')
HaxePsiToken:(('(')
PARAMETER_LIST
<empty list>
HaxePsiToken:)(')')
BLOCK_STATEMENT
HaxePsiToken:{('{')
CALL_EXPRESSION
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('trace')
PsiErrorElement:Missing semicolon.
<empty list>
HaxePsiToken:(('(')
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('Module method );\n}')
PsiErrorElement:')', '.', <operation>, <operator>, CLOSING_QUOTE, LONG_TEMPLATE_ENTRY_START, REGULAR_STRING_PART, SHORT_TEMPLATE_ENTRY_START or is expected
<empty list>
EXPRESSION_LIST
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('Module method')
HaxePsiToken:CLOSING_QUOTE('"')
HaxePsiToken:)(')')
HaxePsiToken:;(';')
HaxePsiToken:}('}')

0 comments on commit dbad976

Please sign in to comment.