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

eu.cqse.check.framework.shallowparser.languages.ada.AdaShallowParser 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.ada;

import static eu.cqse.check.framework.scanner.ETokenType.ABORT;
import static eu.cqse.check.framework.scanner.ETokenType.ABSTRACT;
import static eu.cqse.check.framework.scanner.ETokenType.ACCEPT;
import static eu.cqse.check.framework.scanner.ETokenType.AND;
import static eu.cqse.check.framework.scanner.ETokenType.BEGIN;
import static eu.cqse.check.framework.scanner.ETokenType.BODY;
import static eu.cqse.check.framework.scanner.ETokenType.CASE;
import static eu.cqse.check.framework.scanner.ETokenType.COLON;
import static eu.cqse.check.framework.scanner.ETokenType.DECLARE;
import static eu.cqse.check.framework.scanner.ETokenType.DELAY;
import static eu.cqse.check.framework.scanner.ETokenType.DO;
import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
import static eu.cqse.check.framework.scanner.ETokenType.ELSEIF;
import static eu.cqse.check.framework.scanner.ETokenType.END;
import static eu.cqse.check.framework.scanner.ETokenType.ENDRECORD;
import static eu.cqse.check.framework.scanner.ETokenType.ENTRY;
import static eu.cqse.check.framework.scanner.ETokenType.EQ;
import static eu.cqse.check.framework.scanner.ETokenType.EXCEPTION;
import static eu.cqse.check.framework.scanner.ETokenType.EXIT;
import static eu.cqse.check.framework.scanner.ETokenType.FOR;
import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION;
import static eu.cqse.check.framework.scanner.ETokenType.GENERIC;
import static eu.cqse.check.framework.scanner.ETokenType.GOTO;
import static eu.cqse.check.framework.scanner.ETokenType.GT;
import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
import static eu.cqse.check.framework.scanner.ETokenType.IF;
import static eu.cqse.check.framework.scanner.ETokenType.IS;
import static eu.cqse.check.framework.scanner.ETokenType.LEFT_LABEL_BRACKET;
import static eu.cqse.check.framework.scanner.ETokenType.LOOP;
import static eu.cqse.check.framework.scanner.ETokenType.NEW;
import static eu.cqse.check.framework.scanner.ETokenType.NULL;
import static eu.cqse.check.framework.scanner.ETokenType.OR;
import static eu.cqse.check.framework.scanner.ETokenType.PACKAGE;
import static eu.cqse.check.framework.scanner.ETokenType.PRAGMA;
import static eu.cqse.check.framework.scanner.ETokenType.PREPROCESSOR_DIRECTIVE;
import static eu.cqse.check.framework.scanner.ETokenType.PROCEDURE;
import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED;
import static eu.cqse.check.framework.scanner.ETokenType.RAISE;
import static eu.cqse.check.framework.scanner.ETokenType.RECORD;
import static eu.cqse.check.framework.scanner.ETokenType.RENAMES;
import static eu.cqse.check.framework.scanner.ETokenType.RETURN;
import static eu.cqse.check.framework.scanner.ETokenType.RIGHT_LABEL_BRACKET;
import static eu.cqse.check.framework.scanner.ETokenType.SELECT;
import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;
import static eu.cqse.check.framework.scanner.ETokenType.SEPARATE;
import static eu.cqse.check.framework.scanner.ETokenType.STRING_LITERAL;
import static eu.cqse.check.framework.scanner.ETokenType.SUBTYPE;
import static eu.cqse.check.framework.scanner.ETokenType.TASK;
import static eu.cqse.check.framework.scanner.ETokenType.TERMINATE;
import static eu.cqse.check.framework.scanner.ETokenType.THEN;
import static eu.cqse.check.framework.scanner.ETokenType.TYPE;
import static eu.cqse.check.framework.scanner.ETokenType.USE;
import static eu.cqse.check.framework.scanner.ETokenType.WHEN;
import static eu.cqse.check.framework.scanner.ETokenType.WHILE;
import static eu.cqse.check.framework.scanner.ETokenType.WITH;
import static eu.cqse.check.framework.shallowparser.languages.ada.AdaShallowParser.EAdaParserStates.DECLARATIONS;
import static eu.cqse.check.framework.shallowparser.languages.ada.AdaShallowParser.EAdaParserStates.STATEMENTS;

import java.util.EnumSet;

import eu.cqse.check.framework.scanner.ETokenType;
import eu.cqse.check.framework.scanner.IToken;
import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;

/**
 * Shallow parser for Ada.
 * 

* A good introduction to Ada can be found here: http://en.wikibooks.org/wiki/Ada_Programming *

* A reference is available here: * http://www.adaic.org/resources/add_content/standards/05rm/html/RM-TOC.html *

* What this parser does and does not: *

    *
  • The "with" and "use" statements as well as pragmas are parsed as meta data.
  • *
  • Packages, types, functions, procedures, entries are parsed as expected.
  • *
  • It recognizes the nesting of statements (e.g. in loops), but does not parse into the * statements. For example, it recognizes an if-statement and provides the list of sub-statements, * but does not provide direct access to the if-condition.
  • *
  • The parser does not support non-ASCII characters in identifiers, although Ada95 supports * unicode here. Actually, this is not an issue of the parser but the underlying scanner.
  • *
*

* Implementation hints: *

    *
  • Several rules are registered for any state although they would be expected to occur only in * state DECLARATIONS. This is to allow the parser to recover from state STATEMENTS, if an arbitrary * chunk of Ada code is to be parsed.
  • *
*/ public class AdaShallowParser extends ShallowParserBase { /** The states used in this parser. */ public static enum EAdaParserStates { /** A state to recognize declarations. */ DECLARATIONS, /** A state to recognize statements. */ STATEMENTS } /** Constructor. */ public AdaShallowParser() { super(EAdaParserStates.class, DECLARATIONS); createMetaRules(); createSpecificationRules(); createBodyAndTypeRules(); createMethodAndAttributeRules(); createTypeRules(); createStatementRules(); } /** Create rules for parsing meta elements. */ private void createMetaRules() { // parse generic prefix as meta // we skip from "with" to ";" to not stumble over subprogram parameters inAnyState().sequence(GENERIC).createNode(EShallowEntityType.META, 0) .skipBeforeWithNesting(EnumSet.of(PACKAGE, PROCEDURE, FUNCTION, ENTRY), WITH, SEMICOLON) .endNodeWithContinuation(); // parse use and with as meta inAnyState().sequence(EnumSet.of(WITH, USE, PRAGMA)).createNode(EShallowEntityType.META, 0).skipTo(SEMICOLON) .endNode(); // parse pragma as meta inAnyState().sequence(PREPROCESSOR_DIRECTIVE).createNode(EShallowEntityType.META, "pragma").endNode(); // deal with a dangling end by inserting broken node inAnyState().sequence(END).createNode(EShallowEntityType.META, "dangling end").skipTo(SEMICOLON); // endNode() // omitted! } /** Creates parsing rules for package and task specifications. */ private void createSpecificationRules() { // package specification RecognizerBase packageSpecAlternative = inAnyState().sequence(PACKAGE, IDENTIFIER) .skipBefore(EnumSet.of(SEMICOLON, IS, RENAMES)); packageSpecAlternative.sequence(SEMICOLON).createNode(EShallowEntityType.MODULE, "package specification", -2) .endNode(); packageSpecAlternative.sequence(RENAMES).createNode(EShallowEntityType.MODULE, "package renaming", -2) .skipTo(SEMICOLON).endNode(); packageSpecAlternative.sequence(IS, NEW) .createNode(EShallowEntityType.TYPE, "generic package instantiation", -3).skipTo(SEMICOLON).endNode(); packageSpecAlternative.sequence(IS).createNode(EShallowEntityType.MODULE, "package specification", -2) .parseUntil(DECLARATIONS).sequence(END).skipTo(SEMICOLON).endNode(); // task specification RecognizerBase taskSpecAlternative = inAnyState() .sequence(EnumSet.of(TASK, PROTECTED), IDENTIFIER) .createNode(EShallowEntityType.MODULE, new Object[] { 0, "specification" }, -1) .skipBefore(EnumSet.of(SEMICOLON, IS)); taskSpecAlternative.sequence(SEMICOLON).endNode(); taskSpecAlternative.sequence(IS).parseUntil(DECLARATIONS).sequence(END).skipTo(SEMICOLON).endNode(); } /** * Creates parsing rules for package/task/protected body and task/protected type. */ private void createBodyAndTypeRules() { // package body, task body, protected body RecognizerBase packageBodyAlternative1 = inAnyState() .sequence(EnumSet.of(PACKAGE, TASK, PROTECTED), BODY, IDENTIFIER).skipTo(IS) .createNode(EShallowEntityType.MODULE, new int[] { 0, 1 }, -2); packageBodyAlternative1.sequence(SEPARATE, SEMICOLON).endNode(); RecognizerBase packageBodyAlternative2 = packageBodyAlternative1.parseUntil(DECLARATIONS); packageBodyAlternative2.sequence(END).skipTo(SEMICOLON).endNode(); completeBlock(packageBodyAlternative2.sequence(BEGIN)); // task types and protected types RecognizerBase taskTypeAlternative = inAnyState() .sequence(EnumSet.of(TASK, PROTECTED), TYPE, IDENTIFIER) .createNode(EShallowEntityType.MODULE, new int[] { 0, 1 }).skipBefore(EnumSet.of(SEMICOLON, IS)); taskTypeAlternative.sequence(SEMICOLON).endNode(); taskTypeAlternative.sequence(IS).parseUntil(DECLARATIONS).sequence(END).skipTo(SEMICOLON).endNode(); // new...with is skipped inState(DECLARATIONS).sequence(NEW).skipTo(WITH); } /** * Creates rules for parsing methods (functions, etc.) and attributes (and local variables). */ private void createMethodAndAttributeRules() { // functions, procedures (including operator overloading), entries RecognizerBase functionAlternative = inAnyState() .sequence(EnumSet.of(PROCEDURE, FUNCTION, ENTRY), EnumSet.of(IDENTIFIER, STRING_LITERAL)) .createNode(EShallowEntityType.METHOD, 0, 1).skipBefore(EnumSet.of(SEMICOLON, IS)); functionAlternative.sequence(SEMICOLON).endNode(); functionAlternative.sequence(IS, EnumSet.of(SEPARATE, ABSTRACT, NEW)).skipTo(SEMICOLON).endNode(); completeBlock(functionAlternative.sequence(IS).parseUntil(DECLARATIONS).sequence(BEGIN)); // variables and constants inState(DECLARATIONS).sequence(IDENTIFIER, COLON).createNode(EShallowEntityType.ATTRIBUTE, "variable", 0) .skipTo(SEMICOLON).endNode(); } /** Creates rules for parsing types and similar constructs. */ private void createTypeRules() { // types/subtypes RecognizerBase typeAlternative = inAnyState().sequence(EnumSet.of(TYPE, SUBTYPE), IDENTIFIER) .createNode(EShallowEntityType.TYPE, 0, 1).skipBefore(EnumSet.of(SEMICOLON, IS)); typeAlternative.sequence(SEMICOLON).endNode(); typeAlternative.sequence(IS, NULL, RECORD, SEMICOLON).endNode(); RecognizerBase typeAlternative2 = typeAlternative.sequence(IS) .skipBefore(EnumSet.of(SEMICOLON, RECORD, NULL)); typeAlternative2.sequence(SEMICOLON).endNode(); typeAlternative2.sequence(NULL).skipTo(SEMICOLON).endNode(); typeAlternative2.sequence(RECORD).skipTo(END, ENDRECORD, SEMICOLON).endNode(); // representation clauses RecognizerBase overlayAlternative = inState(DECLARATIONS).sequence(FOR) .createNode(EShallowEntityType.TYPE, "representation clause").skipBefore(EnumSet.of(SEMICOLON, RECORD)); overlayAlternative.sequence(SEMICOLON).endNode(); overlayAlternative.sequence(RECORD).skipTo(END, ENDRECORD, SEMICOLON).endNode(); } /** Creates the rules needed for parsing statements. */ private void createStatementRules() { // if/elseif RecognizerBase ifAlternative = inState(STATEMENTS).sequence(EnumSet.of(IF, ELSEIF)) .createNode(EShallowEntityType.STATEMENT, 0).skipTo(THEN).parseUntil(STATEMENTS) .sequenceBefore(EnumSet.of(ELSEIF, ELSE, END)); ifAlternative.sequence(END).skipTo(SEMICOLON).endNode(); ifAlternative.endNodeWithContinuation(); // else (both for if and select) inState(STATEMENTS).sequence(ELSE).createNode(EShallowEntityType.STATEMENT, 0).parseUntil(STATEMENTS) .sequence(END).skipTo(SEMICOLON).endNode(); // case inState(STATEMENTS).sequence(CASE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(IS).parseUntil(STATEMENTS) .sequence(END).skipTo(SEMICOLON).endNode(); // when (in case, select, and exception handlers) inState(STATEMENTS).sequence(WHEN).createNode(EShallowEntityType.STATEMENT, 0).skipTo(EQ, GT).endNode(); // loop/block labels as meta inState(STATEMENTS).sequence(IDENTIFIER, COLON).sequenceBefore(EnumSet.of(WHILE, FOR, LOOP, BEGIN)) .createNode(EShallowEntityType.META, "Loop name").endNode(); // loops inState(STATEMENTS).sequence(LOOP).createNode(EShallowEntityType.STATEMENT, 0).parseUntil(STATEMENTS) .sequence(END).skipTo(SEMICOLON).endNode(); inState(STATEMENTS).sequence(EnumSet.of(WHILE, FOR)).createNode(EShallowEntityType.STATEMENT, 0).skipTo(LOOP) .parseUntil(STATEMENTS).sequence(END).skipTo(SEMICOLON).endNode(); // blocks completeBlock(inState(STATEMENTS).sequence(IDENTIFIER, COLON, DECLARE) .createNode(EShallowEntityType.STATEMENT, "block").parseUntil(DECLARATIONS).sequence(BEGIN)); completeBlock(inState(STATEMENTS).sequence(DECLARE).createNode(EShallowEntityType.STATEMENT, "block") .parseUntil(DECLARATIONS).sequence(BEGIN)); completeBlock(inState(STATEMENTS).sequence(BEGIN).createNode(EShallowEntityType.STATEMENT, "block")); // accept/do inState(STATEMENTS).sequence(ACCEPT).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DO) .parseUntil(STATEMENTS).sequence(END).skipTo(SEMICOLON).endNode(); // select/or RecognizerBase selectAlternative = inState(STATEMENTS).sequence(EnumSet.of(SELECT, OR, ELSE)) .createNode(EShallowEntityType.STATEMENT, 0).parseUntil(STATEMENTS); selectAlternative.sequenceBefore(EnumSet.of(OR, ELSE)).endNodeWithContinuation(); selectAlternative.sequence(END).skipTo(SEMICOLON).endNode(); // goto labels as meta inState(STATEMENTS).sequence(LEFT_LABEL_BRACKET).skipTo(RIGHT_LABEL_BRACKET) .createNode(EShallowEntityType.META, "goto label").endNode(); // basic statement inState(STATEMENTS).sequence(EnumSet.of(IDENTIFIER, NULL, RETURN, GOTO, EXIT, ABORT, DELAY, RAISE, TERMINATE)) .createNode(EShallowEntityType.STATEMENT, 0).skipTo(SEMICOLON).endNode(); } /** Completes the rule describing blocks. */ private static void completeBlock(RecognizerBase initialSequence) { RecognizerBase alt = initialSequence.parseUntil(STATEMENTS); alt.sequence(EXCEPTION).parseUntil(STATEMENTS).sequence(END).skipTo(SEMICOLON).endNode(); alt.sequence(END).skipTo(SEMICOLON).endNode(); } /** * {@inheritDoc} *

* Maps "and then" and "or else" to simple "and" and "or", as the additional "then" and "else" * keywords may shallow parsing much harder. */ @Override protected boolean isFilteredToken(IToken token, IToken previousToken) { ETokenType previousType = null; if (previousToken != null) { previousType = previousToken.getType(); } ETokenType type = token.getType(); if (previousType == AND && type == THEN) { return true; } if (previousType == OR && type == ELSE) { return true; } return super.isFilteredToken(token, previousToken); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy