All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.cqse.check.framework.shallowparser.languages.dart.DartShallowParser Maven / Gradle / Ivy

Go to download

The Teamscale Custom Check API allows users to extend Teamscale by writing custom analyses that create findings.

There is a newer version: 2024.7.2
Show newest version
/*
 * 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(); } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy