eu.cqse.check.framework.shallowparser.languages.dart.DartShallowParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-check-api Show documentation
Show all versions of teamscale-check-api Show documentation
The Teamscale Custom Check API allows users to extend Teamscale by writing custom analyses that create findings.
/*
* Copyright (c) CQSE GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.cqse.check.framework.shallowparser.languages.dart;
import static eu.cqse.check.framework.scanner.ETokenType.*;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.ANNOTATION;
import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.META;
import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.TYPE;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_ENUM;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_EXPRESSION;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_METHOD;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_MODULE;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_SWITCH_EXPRESSION;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_TYPE;
import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.TOP_LEVEL;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.UnmodifiableSet;
import org.conqat.lib.commons.region.Region;
import eu.cqse.check.framework.scanner.ETokenType;
import eu.cqse.check.framework.scanner.IToken;
import eu.cqse.check.framework.shallowparser.ShallowParserException;
import eu.cqse.check.framework.shallowparser.SubTypeNames;
import eu.cqse.check.framework.shallowparser.TokenStreamUtils;
import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
import eu.cqse.check.framework.shallowparser.languages.base.CStyleShallowParserBase;
import eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates;
/**
* Shallow parser for Dart
*/
public class DartShallowParser extends CStyleShallowParserBase {
private static final UnmodifiableSet VALID_IDENTIFIERS = CollectionUtils
.asUnmodifiable(EnumSet.of(IDENTIFIER, ASYNC, AWAIT, DYNAMIC, GET, LET, SET, VALUE, VAR, RECORD, FINAL,
CONST, QUESTION, FACTORY, LATE, EXTERNAL));
/** All primitive types. */
private static final UnmodifiableSet PRIMITIVE_TYPES = CollectionUtils
.asUnmodifiable(EnumSet.of(VOID, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR, BOOL, STRING, OBJECT, SBYTE,
USHORT, UINT, ULONG, NUM, NUMBER, FUNCTION));
private static final UnmodifiableSet ATTRIBUTE_TOKENS = CollectionUtils
.asUnmodifiable(CollectionUtils.unionSet(EnumSet.of(FINAL, STATIC), new HashSet<>(PRIMITIVE_TYPES)));
public DartShallowParser() {
super(EnumSet.of(TOP_LEVEL));
}
@Override
protected void createSimpleStatementRule() {
super.createSimpleStatementRule();
}
@Override
public List parseTopLevelWithErrors(List tokens) throws ShallowParserException {
// The EOF (end of file) token is needed to finish file-scoped namespaces. It
// has to be added artificially at the end of the token list.
List modifiedTokens = new ArrayList<>(tokens);
if (!tokens.isEmpty()) {
modifiedTokens.add(TokenStreamUtils.createToken(tokens.get(tokens.size() - 1), "", EOF));
}
return super.parseTopLevelWithErrors(modifiedTokens);
}
@Override
protected void createMetaRules() {
// library & parts, see https://stackoverflow.com/a/13879754/3989225
inState(TOP_LEVEL, IN_MODULE).markStart().sequence(LIBRARY).optional(IDENTIFIER).markStart().skipTo(SEMICOLON)
.createNode(EShallowEntityType.META, SubTypeNames.LIBRARY, new Region(0, -2)).endNode();
inState(TOP_LEVEL, IN_MODULE).markStart().sequence(PART, STRING_LITERAL).markStart().skipTo(SEMICOLON)
.createNode(EShallowEntityType.META, SubTypeNames.PART, new Region(0, -2)).endNode();
inState(TOP_LEVEL, IN_MODULE).markStart().sequence(PART, OF, IDENTIFIER).markStart().skipTo(SEMICOLON)
.createNode(EShallowEntityType.META, SubTypeNames.PART_OF, new Region(0, -2)).endNode();
// imports
inState(TOP_LEVEL, IN_MODULE).sequence(IMPORT).skipTo(SEMICOLON).createNode(META, 0, new Region(1, -2))
.endNode();
// @ annotations / @pragma
inAnyState().sequence(PRAGMA).repeated(DOT, IDENTIFIER).createNode(META, ANNOTATION, "pragma")
.skipNested(LPAREN, RPAREN).endNode();
inAnyState().sequence(OVERRIDE).repeated(DOT, IDENTIFIER).createNode(META, ANNOTATION, "override")
.skipNested(LPAREN, RPAREN).endNode();
inAnyState().sequence(AT, IDENTIFIER).repeated(DOT, IDENTIFIER).createNode(META, ANNOTATION, new Region(1, -1))
.skipNested(LPAREN, RPAREN).endNode();
super.createMetaRules();
}
@Override
protected void createTypeRules() {
createEnumTypeRule();
inState(TOP_LEVEL).markStart().sequence(EXTENSION, IDENTIFIER).skipBefore(EnumSet.of(SEMICOLON, LBRACE))
.sequence(LBRACE).createNode(EShallowEntityType.TYPE, "extension", 1).parseUntil(IN_TYPE)
.sequence(RBRACE).endNode();
super.createTypeRules();
}
private void createEnumTypeRule() {
RecognizerBase typeNode = inState(TOP_LEVEL, IN_TYPE).repeated(getTypeModifier())
.sequence(ENUM, IDENTIFIER).createNode(TYPE, SubTypeNames.ENUM, -1);
RecognizerBase inEnumRule = typeNode.skipTo(LBRACE).parseUntil(IN_ENUM);
inEnumRule.sequence(SEMICOLON).parseUntil(IN_TYPE).sequence(RBRACE).endNode();
inEnumRule.sequence(RBRACE).endNode();
}
@Override
protected EnumSet getTypeKeywords() {
return EnumSet.of(CLASS, INTERFACE, ENUM, STRUCT, MIXIN, ETokenType.TYPE, EXTENSION, SEALED);
}
@Override
protected EnumSet getTypeModifier() {
return EnumSet.of(PUBLIC, PRIVATE, ABSTRACT, STATIC, FINAL, EXTENSION, BASE);
}
@Override
protected void createBasicBlockRules() {
// try { } on SomeException catch (e) { }
inState(IN_METHOD).sequence(ON, IDENTIFIER, CATCH, LPAREN, IDENTIFIER, RPAREN, LBRACE)
.createNode(EShallowEntityType.STATEMENT, SubTypeNames.CATCH).parseOnce(IN_METHOD).skipTo(RBRACE)
.endNode();
createBasicBlockRule(getBlockRuleStart(), EnumSet.of(TRY, ON), EnumSet.of(CATCH, FINALLY, DYNAMIC, ON), true,
false, 0);
super.createBasicBlockRules();
}
@Override
protected Set getOptionalTokensBeforeBlockBraces() {
// Handles this case: try {} on FormatException {}
// (Note the missing braces)
return EnumSet.of(IDENTIFIER, ON);
}
/** Returns both type and type member modifiers */
private EnumSet getTypeAndMemberModifiers() {
EnumSet allModifiers = getTypeModifier();
allModifiers.addAll(EnumSet.of(PROTECTED, ASYNC, CONST, EXTERN, OVERRIDE, NEW));
return allModifiers;
}
@Override
protected void createClassElementsRules() {
// _LineSummary() : foo = bar, test = 'baz' { ... }
inState(IN_TYPE, IN_ENUM).skipBefore(getValidIdentifiers()).markStart().sequence(IDENTIFIER, LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(COLON)
.skipAny(EnumSet.of(SUPER, IDENTIFIER, EQ, STRING_LITERAL, INTEGER_LITERAL, DOT, LPAREN, RPAREN))
.sequence(LBRACE).createNode(EShallowEntityType.METHOD, "method", 0).parseUntil(IN_METHOD)
.sequence(RBRACE).endNode();
// _LineSummary() : super._();
inState(IN_TYPE, IN_ENUM).skipBefore(getValidIdentifiers()).markStart().sequence(IDENTIFIER, LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(COLON)
.skipAny(EnumSet.of(SUPER, IDENTIFIER, EQ, STRING_LITERAL, INTEGER_LITERAL, DOT, LPAREN, RPAREN))
.createNode(EShallowEntityType.METHOD, "method", 0).skipTo(SEMICOLON).endNode();
completeMethod("method", EShallowEntityType.METHOD, IN_METHOD,
typePatternInState(IN_TYPE, IN_ENUM).sequence(getValidIdentifiers()).markStart()
.repeated(IDENTIFIER, DOT).sequence(getValidIdentifiers()).skipNested(LT, GT).sequence(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN));
// Map toMap() => { 'test': 'bar' };
inState(IN_TYPE, TOP_LEVEL).optional(getValidIdentifiers()).skipNested(LT, GT).markStart()
.sequence(IDENTIFIER, LPAREN).skipTo(RPAREN).sequence(DOUBLE_ARROW, LBRACE, STRING_LITERAL)
.createNode(EShallowEntityType.METHOD, "method", 0).skipTo(SEMICOLON).endNode();
// S.result(String result) : result = result, b = null { ... }
inState(IN_TYPE).skipBefore(getValidIdentifiers()).markStart().sequence(IDENTIFIER).optional(DOT, IDENTIFIER)
.sequence(LPAREN).skipTo(RPAREN).sequence(COLON).skipTo(LBRACE)
.createNode(EShallowEntityType.METHOD, "method", 0).parseOnce(IN_METHOD).skipTo(RBRACE).endNode();
// Methods without parenthesis:
// int get foo { return 42; }
inState(IN_TYPE, TOP_LEVEL).skipBefore(getValidIdentifiers()).markStart().sequence(IDENTIFIER, LBRACE)
.parseUntil(IN_METHOD).skipToWithNesting(RBRACE, LBRACE, RBRACE)
.createNode(EShallowEntityType.METHOD, SubTypeNames.METHOD, 0).endNode();
// Methods without explicit return type, e.g. 'read(int bytes);'
inState(IN_TYPE, TOP_LEVEL).skipBefore(getValidIdentifiers()).markStart()
.sequence(getValidIdentifiers(), LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(SEMICOLON)
.createNode(EShallowEntityType.METHOD, "method", 0).endNode();
// some_test() async { ... }
inState(IN_TYPE, TOP_LEVEL).skipBefore(getValidIdentifiers()).markStart().sequence(IDENTIFIER, LPAREN)
.skipTo(RPAREN).sequence(ASYNC, LBRACE).createNode(EShallowEntityType.METHOD, "method", 0)
.parseUntil(IN_METHOD).sequence(RBRACE).endNode();
// Top-Level methods (not part of any class)
// void main() => runApp(MyApp());
inState(TOP_LEVEL).skipBefore(getValidIdentifiers()).markStart().sequence(getValidIdentifiers(), LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(DOUBLE_ARROW)
.createNode(EShallowEntityType.METHOD, "method", 0).parseOnce(IN_METHOD).endNode();
inState(TOP_LEVEL).skipBefore(getValidIdentifiers()).markStart().sequence(getValidIdentifiers(), LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).skipTo(LBRACE)
.createNode(EShallowEntityType.METHOD, "method", 0).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
// bool get is42 => { return 42; }
inState(IN_TYPE, IN_ENUM).skipBefore(getValidIdentifiers()).markStart()
.sequence(IDENTIFIER, DOUBLE_ARROW, LBRACE).skipToWithNesting(RBRACE, LBRACE, RBRACE)
.createNode(EShallowEntityType.METHOD, SubTypeNames.LAMBDA, 0).endNode();
// bool get isNew => _isNew;
inState(IN_TYPE, IN_ENUM).skipBefore(getValidIdentifiers()).markStart().sequence(IDENTIFIER, DOUBLE_ARROW)
.createNode(EShallowEntityType.METHOD, "lambda", 0).parseUntil(IN_METHOD).skipTo(SEMICOLON).endNode();
createAttributeRules();
// enum attribute calls: car(tires: 4, passengers: 5)[,]
inState(IN_ENUM).markStart().sequence(IDENTIFIER, LPAREN).skipTo(RPAREN).optional(COMMA)
.createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ENUM_LITERAL, 0).endNode();
// simple enum literals
inState(IN_ENUM).markStart().sequence(IDENTIFIER).optional(COMMA)
.createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ENUM_LITERAL, 0).endNode();
// Operator overrides
createOperatorOverrideRules();
// other methods
createMemberMethodRules();
}
private void createAttributeRules() {
// Attributes:
// List _pendingData;
inState(IN_TYPE).optional(ATTRIBUTE_TOKENS).skipNested(LT, GT).markStart().sequence(IDENTIFIER, SEMICOLON)
.createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, 0).endNode();
// bool digestCalled = false;
inState(IN_TYPE).optional(ATTRIBUTE_TOKENS).skipNested(LT, GT).markStart().sequence(IDENTIFIER, EQ)
.skipTo(SEMICOLON).createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, 0).endNode();
// static const _r = [ ... ];
inState(IN_TYPE).optional(ATTRIBUTE_TOKENS).markStart().sequence(EQ, LBRACK).skipTo(RBRACK)
.createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, -1).endNode();
}
private void createOperatorOverrideRules() {
// operator ==(Object other) =>
inState(IN_TYPE, IN_ENUM).optional(getValidIdentifiers()).sequence(OPERATOR).skipTo(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(DOUBLE_ARROW).skipTo(SEMICOLON)
.createNode(EShallowEntityType.METHOD, "operator").endNode();
// e.g. String operator +(String other) { ... }
inState(IN_TYPE, IN_ENUM).optional(getValidIdentifiers()).sequence(OPERATOR).skipTo(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(LBRACE)
.createNode(EShallowEntityType.METHOD, "operator").parseUntil(IN_METHOD).sequence(RBRACE).endNode();
// e.g. bool operator ==(String other);
inState(IN_TYPE, IN_ENUM).optional(getValidIdentifiers()).sequence(OPERATOR).skipTo(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(SEMICOLON)
.createNode(EShallowEntityType.METHOD, "operator").endNode();
}
/**
* Creates a new recognizer that can match "<...>" followed by a random sequence of "?", "*" and
* "[...]". It is intended to match the suffixes of a type. The suffixes don't have to be
* syntactically correct or make real sense. Two consecutive "?" are not permitted.
*
* Examples:
*
[,,,]?
* []*?
* ?*[]*
*/
private RecognizerBase createTypeSuffixRecognizer() {
RecognizerBase recognizer = createRecognizer(
start -> start.optional(QUESTION).optional(MULT).skipNested(LBRACK, RBRACK));
return createRecognizer(start -> start.skipNested(LT, GT).repeatedSubRecognizer(recognizer));
}
/** Rules for member methods */
private void createMemberMethodRules() {
// func1() { }
inState(IN_TYPE).markStart().sequence(IDENTIFIER, LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN)
.sequence(LBRACE).createNode(EShallowEntityType.METHOD, "method", 0).parseUntil(IN_METHOD)
.sequence(RBRACE).endNode();
// Methods without return type and no body (= abstract)
inState(IN_TYPE).markStart().sequence(IDENTIFIER, LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN)
.sequence(SEMICOLON).createNode(EShallowEntityType.METHOD, "method", 0).endNode();
// Getter
inState(IN_TYPE, IN_ENUM).optional(getValidIdentifiers()).skipNested(LT, GT).markStart()
.sequence(GET, IDENTIFIER, LBRACE).createNode(EShallowEntityType.METHOD, SubTypeNames.GET, 1)
.parseUntil(IN_METHOD).sequence(RBRACE).endNode();
inState(IN_TYPE, IN_ENUM).optional(getValidIdentifiers()).skipNested(LT, GT).markStart()
.sequence(GET, IDENTIFIER, LBRACE).skipTo(RBRACE)
.createNode(EShallowEntityType.METHOD, SubTypeNames.GET, 1).endNode();
// Getter with '=>' lambda
inState(IN_TYPE, IN_ENUM).optional(getValidIdentifiers()).sequence(GET, IDENTIFIER, DOUBLE_ARROW)
.skipTo(SEMICOLON).createNode(EShallowEntityType.METHOD, SubTypeNames.GET, 2).endNode();
// Setter
inState(IN_TYPE).optional(getValidIdentifiers()).sequence(SET, IDENTIFIER, LPAREN).skipTo(RBRACE)
.createNode(EShallowEntityType.METHOD, SubTypeNames.SET, 2).endNode();
completeMethod(SubTypeNames.METHOD, EShallowEntityType.METHOD, IN_METHOD,
typePatternInState(IN_TYPE, TOP_LEVEL, IN_ENUM)
.repeatedSubRecognizer(createExplicitInterfaceQualifierRecognizer()).markStart()
.sequence(PRIMITIVE_TYPES).skipNested(LT, GT).sequence(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN));
completeMethod(SubTypeNames.METHOD, EShallowEntityType.METHOD, IN_METHOD,
typePatternInState(IN_TYPE, TOP_LEVEL, IN_ENUM)
.repeatedSubRecognizer(createExplicitInterfaceQualifierRecognizer()).markStart()
.sequence(getValidIdentifiers()).skipNested(LT, GT).sequence(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN));
}
/**
* Creates a new recognizer that can match an explicit interface qualifier prefix for a method-like
* construct. This includes sequences of identifiers with dots, possibly intermixed with template
* arguments.
*/
private RecognizerBase createExplicitInterfaceQualifierRecognizer() {
// remember the start of the recognizer chain (we can not use the
// result of the method chain, as this would be the last recognizer)
return createRecognizer(start -> start.sequence(getValidIdentifiers()).skipNested(LT, GT).sequence(DOT));
}
/**
* Completes a method-like construct. This begins with searching for the first semicolon or brace,
* i.e., the parameter list should already be skipped. This ends either in a complete method with a
* body, or with a semicolon and thus is just an abstract method.
*/
private static void completeMethod(String subtype, EShallowEntityType nodeType, Object name,
EGenericParserStates subParseState, RecognizerBase start) {
RecognizerBase alternative = start
.skipBefore(EnumSet.of(LBRACE, SEMICOLON, DOUBLE_ARROW, EQ));
alternative.sequence(LBRACE).createNode(nodeType, subtype, name).parseUntil(subParseState).sequence(RBRACE)
.endNode();
alternative.sequence(SEMICOLON).createNode(nodeType, subtype, name).endNode();
// for lambdas, we always parse IN_METHOD and ignore the subParseState
RecognizerBase lambdaAlternative = alternative.sequence(DOUBLE_ARROW).createNode(nodeType,
subtype, name);
lambdaAlternative.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
lambdaAlternative.parseOnce(IN_METHOD).endNode();
// lambdas with "=" instead of "=>"
RecognizerBase equalsAlternative = alternative.sequence(EQ).createNode(nodeType, subtype,
name);
equalsAlternative.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
equalsAlternative.parseOnce(IN_METHOD).endNode();
}
private static void completeMethod(String subtype, EShallowEntityType nodeType, EGenericParserStates subParseState,
RecognizerBase start) {
completeMethod(subtype, nodeType, 0, subParseState, start);
}
/**
* {@inheritDoc} Also returns all contextual keywords
*/
@Override
public Set getValidIdentifiers() {
return VALID_IDENTIFIERS;
}
@Override
protected EnumSet getSimpleBlockKeywordsWithParentheses() {
return EnumSet.of(WHILE, FOR, SWITCH, LOCK);
}
@Override
public RecognizerBase typePattern(RecognizerBase currentState) {
EnumSet modifierKeywords = getTypeAndMemberModifiers();
EnumSet typeStart = EnumSet.copyOf(PRIMITIVE_TYPES);
typeStart.addAll(getValidIdentifiers());
// the repeated (DOT, typeStart) is used for full qualified type names
RecognizerBase simpleTypeRecognizer = createRecognizer(start -> start.sequence(typeStart)
.repeated(DOT, typeStart).skipNested(LT, GT).optionalSubRecognizer(createTypeSuffixRecognizer()));
RecognizerBase tupleTypeRecognizer = createRecognizer(start -> start.sequence(LPAREN)
.skipToWithNesting(RPAREN, LPAREN, RPAREN).optionalSubRecognizer(createTypeSuffixRecognizer()));
RecognizerBase typeRecognizer = createRecognizer(
start -> start.subRecognizer(simpleTypeRecognizer));
typeRecognizer.subRecognizer(tupleTypeRecognizer);
return currentState.repeated(modifierKeywords).subRecognizer(typeRecognizer);
}
@Override
protected EnumSet getSimpleBlockKeywordsWithoutParentheses() {
return EnumSet.of(ELSE, FINALLY);
}
@Override
public EnumSet getStatementStartTokens() {
return EnumSet.of(NEW, BREAK, CONTINUE, RETURN, ASSERT, CONST, GOTO, THROW, THIS, CHECKED, SIZEOF, TYPEOF,
VALUE, YIELD, LPAREN, PLUSPLUS, MINUSMINUS, NOT, PLUS, MINUS, COMP, TRUE, FALSE, BOOLEAN_LITERAL,
INTEGER_LITERAL, FLOATING_POINT_LITERAL, STRING_LITERAL, IDENTIFIER, MULT, DEFAULT, SUPER, LBRACK,
LBRACE, LT, RETHROW);
}
@Override
protected RecognizerBase getBlockRuleStart() {
return super.getBlockRuleStart().optional(AWAIT).markStart();
}
@Override
protected void createSubExpressionRules() {
super.createSubExpressionRules();
createSwitchExpressionRules();
createLambdaWithArrowRules(DOUBLE_ARROW);
}
@Override
public RecognizerBase getSubExpressionRecognizer() {
return new DartSubExpressionRecognizer();
}
private void createSwitchExpressionRules() {
// switch expressions; we are currently not including the leading range
// expression
inState(IN_EXPRESSION).sequence(SWITCH).skipTo(LBRACE)
.createNode(EShallowEntityType.STATEMENT, SubTypeNames.SWITCH_EXPRESSION, new Region(0, 0))
.parseUntil(IN_SWITCH_EXPRESSION).sequence(RBRACE).endNode();
// switch expression case right-hand side (starts with '=>'), handled as lambda
// expression
inState(IN_SWITCH_EXPRESSION).preCondition(createRecognizer(start -> start.sequence(DOUBLE_ARROW)))
.parseOnce(IN_EXPRESSION).optional(COMMA);
// switch expression case left-hand side (ends before '=>'), excludes '=>' as
// this is needed to recognize the right-hand side as lambda expression
inState(IN_SWITCH_EXPRESSION).skipBefore(DOUBLE_ARROW)
.createNode(EShallowEntityType.META, SubTypeNames.CASE, new Region(0, -1)).endNode();
}
}