eu.cqse.check.framework.shallowparser.languages.ada.AdaShallowParser Maven / Gradle / Ivy
Show all versions of teamscale-check-api Show documentation
/*
* 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);
}
}