You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

692 lines
21 KiB

//==============================================================================
//
// Copyright (c) 2015-
// Authors:
// * Joachim Klein <klein@tcs.inf.tu-dresden.de>
// * David Mueller <david.mueller@tcs.inf.tu-dresden.de>
//
//------------------------------------------------------------------------------
//
// This file is part of the cpphoafparser library,
// http://automata.tools/hoa/cpphoafparser/
//
// The cpphoafparser library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// The cpphoafparser library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//==============================================================================
#ifndef CPPHOAFPARSER_HOAPARSER_H
#define CPPHOAFPARSER_HOAPARSER_H
#include <set>
#include <string>
#include <sstream>
#include "cpphoafparser/parser/hoa_lexer.hh"
#include "cpphoafparser/consumer/hoa_consumer.hh"
#include "cpphoafparser/consumer/hoa_intermediate_check_validity.hh"
#include "cpphoafparser/consumer/hoa_intermediate_resolve_aliases.hh"
#include "cpphoafparser/parser/hoa_parser_exception.hh"
/** @mainpage cpphoafparser API documentation
*
* API documentation for the <a href="http://automata.tools/hoa/cpphoafparser/">cpphoafparser</a> library.
*/
/** @namespace cpphoafparser
* The `cpphoafparser` namespace contains all the classes of the
* cpphoafparser library.
*/
namespace cpphoafparser {
/**
* Parser class for parsing HOA files.
*
* Provides a static function for parsing a HOA automaton from an input stream,
* calling into a HOAConsumer for every syntax element encountered during the parse.
*
* The parser is implemented as a simple, hand-written recursive-descent parser,
* with functions corresponding to grammar rules.
**/
class HOAParser {
public:
/**
* Function for parsing a single HOA automaton.
*
* On error, will throw HOAParserException, the consumers will generally throw
* HOAConsumerException.
*
* @param in std::istream from which the automaton will be read
* @param consumer a shared_ptr to the HOAConsumer whose functions will
* be called for each element of the HOA automaton
* @param check_validity Should the validity of the HOA be checked?
* These are checks beyond the basic syntactic well-formedness guaranteed by the grammar.
**/
static void parse(std::istream& in, HOAConsumer::ptr consumer, bool check_validity=true) {
if (consumer->parserResolvesAliases()) {
consumer.reset(new HOAIntermediateResolveAliases(consumer));
}
if (check_validity) {
consumer.reset(new HOAIntermediateCheckValidity(consumer));
}
HOAParser parser(in, consumer);
parser.nextToken();
parser.Automaton();
}
private:
/** The registered consumer */
HOAConsumer::ptr consumer;
/** The lexer for tokenizing the input stream */
HOALexer lexer;
/** The current token */
HOALexer::Token token;
/** true if we are currently in a State: definition */
bool inState;
/** the index of the current state*/
unsigned int currentState;
/** true if the current state has state labeling */
bool currentStateHasStateLabel;
/** Private constructor. */
HOAParser(std::istream& in, HOAConsumer::ptr consumer) :
consumer(consumer), lexer(in), inState(false), currentState(0), currentStateHasStateLabel(false) {
}
/** Advance to the next token. Handles TOKEN_ABORT */
void nextToken() {
token = lexer.nextToken();
if (token.kind == HOALexer::TOKEN_ABORT) {
consumer->notifyAbort();
throw "aborted";
}
}
/** Advances to the next token if it is of the expected kind, otherwise throw an error. */
void expect(HOALexer::TokenType kind, const std::string& context="") {
if (token.kind != kind) {
throw error(HOALexer::Token::forErrorMessage(kind), context);
}
// eat token
nextToken();
}
/**
* Constructs a HOAParserExeption for a syntax error.
* @param expectedTokenTypes a string detailing which token types were expected
* @param context optionally, some context for the error message */
HOAParserException error(const std::string& expectedTokenTypes, const std::string& context="") {
std::stringstream ss;
ss << "Syntax error";
if (context != "") {
ss << " (while reading " << context << ")";
}
ss << ": Expected " << expectedTokenTypes;
ss << ", got " << HOALexer::Token::forErrorMessage(token);
ss << " (line " << std::to_string(token.line) << ", col " << std::to_string(token.col) << ")";
return HOAParserException(ss.str(), token.line, token.col);
}
/** Grammar rule for the whole Automaton */
void Automaton() {
Header();
expect(HOALexer::TOKEN_BODY);
consumer->notifyBodyStart();
Body();
expect(HOALexer::TOKEN_END);
if (inState) {
consumer->notifyEndOfState(currentState);
}
consumer->notifyEnd();
}
/** Grammar rule for the HOA header */
void Header() {
Format();
HeaderItems();
}
/** Grammar rule for the HOA: header */
void Format() {
expect(HOALexer::TOKEN_HOA);
std::string version = Identifier("version");
// TODO: check format version
consumer->notifyHeaderStart(version);
}
/** Grammar rule for the remaining header items, returns if there are not more headers */
void HeaderItems() {
while (true) {
switch (token.kind) {
case HOALexer::TOKEN_STATES: HeaderItemStates(); break;
case HOALexer::TOKEN_START: HeaderItemStart(); break;
case HOALexer::TOKEN_AP: HeaderItemAP(); break;
case HOALexer::TOKEN_ALIAS: HeaderItemAlias(); break;
case HOALexer::TOKEN_ACCEPTANCE: HeaderItemAcceptance(); break;
case HOALexer::TOKEN_ACCNAME: HeaderItemAccName(); break;
case HOALexer::TOKEN_TOOL: HeaderItemTool(); break;
case HOALexer::TOKEN_NAME: HeaderItemName(); break;
case HOALexer::TOKEN_PROPERTIES: HeaderItemProperties(); break;
case HOALexer::TOKEN_HEADER_NAME: HeaderMiscItem(); break;
default:
// not a header, return
return;
}
}
}
/** Grammar rule for the States-header */
void HeaderItemStates() {
expect(HOALexer::TOKEN_STATES);
unsigned int states = Integer("number of states in States-header");
consumer->setNumberOfStates(states);
}
/** Grammar rule for the Start-header */
void HeaderItemStart() {
expect(HOALexer::TOKEN_START);
std::vector<unsigned int> stateConjunction;
unsigned int state = Integer("Start: index of start state");
stateConjunction.push_back(state);
while (token.kind == HOALexer::TOKEN_AND) {
expect(HOALexer::TOKEN_AND);
state = Integer("Start: index of start state in conjunction");
stateConjunction.push_back(state);
}
consumer->addStartStates(stateConjunction);
}
/** Grammar rule for the AP-header */
void HeaderItemAP() {
expect(HOALexer::TOKEN_AP);
unsigned int apCount = Integer("AP: number of atomic propositions");
std::vector<std::string> apList;
std::set<std::string> aps;
while (token.kind == HOALexer::TOKEN_STRING) {
std::string ap = QuotedString();
if (aps.find(ap) != aps.end()) {
throw HOAConsumerException("Atomic proposition \""+ap+"\" is a duplicate!");
}
aps.insert(ap);
apList.push_back(ap);
}
if (apList.size() != apCount) {
throw HOAConsumerException("Number of provided APs (" + std::to_string(apList.size()) + ") does not match number of APs that was specified (" + std::to_string(apCount) + ")");
}
consumer->setAPs(apList);
}
/** Grammar rule for the Alias-header */
void HeaderItemAlias() {
expect(HOALexer::TOKEN_ALIAS);
std::string aliasName = AliasName();
HOAConsumer::label_expr::ptr labelExpr = LabelExpr();
consumer->addAlias(aliasName, labelExpr);
}
/** Grammar rule for the Acceptance-header */
void HeaderItemAcceptance() {
expect(HOALexer::TOKEN_ACCEPTANCE);
unsigned int numberOfSets = Integer("Acceptance: number of acceptance sets");
HOAConsumer::acceptance_expr::ptr accExpr = AcceptanceCondition();
consumer->setAcceptanceCondition(numberOfSets, accExpr);
}
/** Grammar rule for the acc-name-header */
void HeaderItemAccName() {
expect(HOALexer::TOKEN_ACCNAME);
std::string accName = Identifier("acceptance name");
std::vector<IntOrString> extraInfo;
while (true) {
if (token.kind == HOALexer::TOKEN_IDENT) {
extraInfo.push_back(IntOrString(Identifier()));
} else if (token.kind == HOALexer::TOKEN_INT) {
extraInfo.push_back(IntOrString(Integer()));
} else if (token.kind == HOALexer::TOKEN_TRUE) {
extraInfo.push_back(IntOrString("t"));
expect(HOALexer::TOKEN_TRUE); // munch
} else if (token.kind == HOALexer::TOKEN_FALSE) {
extraInfo.push_back(IntOrString("f"));
expect(HOALexer::TOKEN_FALSE); // munch
} else {
break;
}
// TODO
// if (settings == null || !settings.getFlagIgnoreAccName()) {
// consumer.provideAcceptanceName(accName, extraInfo);
//}
}
consumer->provideAcceptanceName(accName, extraInfo);
}
/** Grammar rule for the tool-header */
void HeaderItemTool() {
expect(HOALexer::TOKEN_TOOL);
std::string tool = QuotedString();
std::shared_ptr<std::string> version;
if (token.kind == HOALexer::TOKEN_STRING) {
version.reset(new std::string(QuotedString()));
}
consumer->setTool(tool, version);
}
/** Grammar rule for the name-header */
void HeaderItemName() {
expect(HOALexer::TOKEN_NAME);
std::string name = QuotedString();
consumer->setName(name);
}
/** Grammar rule for the properties-header */
void HeaderItemProperties() {
expect(HOALexer::TOKEN_PROPERTIES);
std::vector<std::string> properties;
while (true) {
if (token.kind == HOALexer::TOKEN_IDENT) {
std::string property = Identifier();
properties.push_back(property);
} else if (token.kind == HOALexer::TOKEN_TRUE) {
// t does not have the special boolean meaning here, back to string
properties.push_back("t");
expect(HOALexer::TOKEN_TRUE); // eat
} else if (token.kind == HOALexer::TOKEN_FALSE) {
// f does not have the special boolean meaning here, back to string
properties.push_back("f");
expect(HOALexer::TOKEN_FALSE); // eat
} else {
// no more properties...
break;
}
}
consumer->addProperties(properties);
}
/** Grammar rule for a misc header (not known from the format specification) */
void HeaderMiscItem() {
std::string headerName = token.vString;
headerName = headerName.substr(0, headerName.length()-1);
expect(HOALexer::TOKEN_HEADER_NAME);
std::vector<IntOrString> content;
while (true) {
if (token.kind == HOALexer::TOKEN_INT) {
content.push_back(Integer());
} else if (token.kind == HOALexer::TOKEN_IDENT) {
content.push_back(Identifier());
} else if (token.kind == HOALexer::TOKEN_STRING) {
content.push_back(IntOrString(QuotedString(), true));
} else if (token.kind == HOALexer::TOKEN_TRUE) {
// t does not have the special boolean meaning here, back to string
content.push_back(IntOrString("t", false));
expect(HOALexer::TOKEN_TRUE); // eat
} else if (token.kind == HOALexer::TOKEN_FALSE) {
// f does not have the special boolean meaning here, back to string
content.push_back(IntOrString("f", false));
expect(HOALexer::TOKEN_FALSE); // eat
} else {
break;
}
}
consumer->addMiscHeader(headerName, content);
}
/** Grammar rule for the automaton body */
void Body() {
while (true) {
switch (token.kind) {
case HOALexer::TOKEN_STATE:
StateName();
break;
case HOALexer::TOKEN_END:
return;
case HOALexer::TOKEN_EOF:
return;
default:
if (inState) {
Edge();
} else {
throw error("either State: or --END--");
}
}
}
}
/** Grammar rule for the State definition */
void StateName() {
expect(HOALexer::TOKEN_STATE);
HOAConsumer::label_expr::ptr labelExpr;
std::shared_ptr<std::string> stateComment;
std::shared_ptr<HOAConsumer::int_list> accSignature;
if (token.kind == HOALexer::TOKEN_LBRACKET) {
labelExpr = Label();
}
unsigned int state = Integer(); // name of the state
if (token.kind == HOALexer::TOKEN_STRING) {
stateComment.reset(new std::string(QuotedString())); // state comment
}
if (token.kind == HOALexer::TOKEN_LCURLY) {
accSignature = AcceptanceSignature();
}
if (inState) {
consumer->notifyEndOfState(currentState);
}
consumer->addState(state, stateComment, labelExpr, accSignature);
// store global information:
inState = true;
currentState = state;
currentStateHasStateLabel = (bool)(labelExpr);
}
/** Grammar rule for an automaton edge */
void Edge() {
HOAConsumer::label_expr::ptr labelExpr;
std::shared_ptr<HOAConsumer::int_list> conjStates;
std::shared_ptr<HOAConsumer::int_list> accSignature;
if (token.kind == HOALexer::TOKEN_LBRACKET) {
labelExpr = Label();
}
conjStates = StateConjunction("edge");
if (token.kind == HOALexer::TOKEN_LCURLY) {
accSignature = AcceptanceSignature();
}
if (labelExpr || currentStateHasStateLabel) {
consumer->addEdgeWithLabel(currentState, labelExpr, *conjStates, accSignature);
} else {
consumer->addEdgeImplicit(currentState, *conjStates, accSignature);
}
}
/**
* Grammar rule for a state conjunction
* @param context contextual information for error messages
*/
std::shared_ptr<HOAConsumer::int_list> StateConjunction(const std::string& context) {
std::shared_ptr<HOAConsumer::int_list> stateConjunction (new HOAConsumer::int_list());
unsigned int state = Integer(context);
stateConjunction->push_back(state);
while (token.kind == HOALexer::TOKEN_AND) {
expect(HOALexer::TOKEN_AND);
state = Integer(context);
stateConjunction->push_back(state);
}
return stateConjunction;
}
/** Grammar rule for a [label-expr] */
HOAConsumer::label_expr::ptr Label() {
HOAConsumer::label_expr::ptr result;
expect(HOALexer::TOKEN_LBRACKET);
result = LabelExpr();
expect(HOALexer::TOKEN_RBRACKET);
return result;
}
/** Grammar rule for an acceptance signature */
std::shared_ptr<HOAConsumer::int_list> AcceptanceSignature() {
std::shared_ptr<HOAConsumer::int_list> result(new HOAConsumer::int_list());
expect(HOALexer::TOKEN_LCURLY);
while (token.kind == HOALexer::TOKEN_INT) {
unsigned int accSet = Integer();
result->push_back(accSet);
}
expect(HOALexer::TOKEN_RCURLY);
return result;
}
/** Grammar rule for an acceptance condition expression (handle disjunction)*/
HOAConsumer::acceptance_expr::ptr AcceptanceCondition() {
HOAConsumer::acceptance_expr::ptr left = AcceptanceConditionAnd();
while (token.kind == HOALexer::TOKEN_OR) {
expect(HOALexer::TOKEN_OR);
HOAConsumer::acceptance_expr::ptr right = AcceptanceConditionAnd();
left = left | right;
}
return left;
}
/** Grammar rule for conjunction in an acceptance condition */
HOAConsumer::acceptance_expr::ptr AcceptanceConditionAnd() {
HOAConsumer::acceptance_expr::ptr left = AcceptanceConditionAtom();
while (token.kind == HOALexer::TOKEN_AND) {
expect(HOALexer::TOKEN_AND);
HOAConsumer::acceptance_expr::ptr right = AcceptanceConditionAtom();
left = left & right;
}
return left;
}
/** Grammar rule for the atoms in an acceptance condition */
HOAConsumer::acceptance_expr::ptr AcceptanceConditionAtom() {
HOAConsumer::acceptance_expr::ptr result;
switch (token.kind) {
case HOALexer::TOKEN_LPARENTH:
expect(HOALexer::TOKEN_LPARENTH);
result = AcceptanceCondition();
expect(HOALexer::TOKEN_RPARENTH);
return result;
case HOALexer::TOKEN_TRUE:
expect(HOALexer::TOKEN_TRUE);
result.reset(new HOAConsumer::acceptance_expr(true));
return result;
case HOALexer::TOKEN_FALSE:
expect(HOALexer::TOKEN_FALSE);
result.reset(new HOAConsumer::acceptance_expr(false));
return result;
case HOALexer::TOKEN_IDENT:
result.reset(new HOAConsumer::acceptance_expr(AcceptanceConditionTemporalOperator()));
return result;
default:
throw error("acceptance condition");
}
}
/** Grammar rule for a temporal operator (Fin/Inf) in an acceptance condition */
AtomAcceptance::ptr AcceptanceConditionTemporalOperator() {
AtomAcceptance::AtomType atomType = AtomAcceptance::TEMPORAL_FIN;
bool negated = false;
unsigned int accSetIndex;
std::string temporalOperator = Identifier();
if (temporalOperator == "Fin") {
atomType = AtomAcceptance::TEMPORAL_FIN;
} else if (temporalOperator == "Inf") {
atomType = AtomAcceptance::TEMPORAL_INF;
} else {
throw error("either 'Fin' or 'Inf'", "acceptance condition");
}
expect(HOALexer::TOKEN_LPARENTH, "acceptance condition");
if (token.kind == HOALexer::TOKEN_NOT) {
expect(HOALexer::TOKEN_NOT);
negated = true;
}
accSetIndex = Integer("acceptance set index");
expect(HOALexer::TOKEN_RPARENTH, "acceptance condition");
return AtomAcceptance::ptr(new AtomAcceptance(atomType, accSetIndex, negated));
}
/** Grammar rule for a label expression (handle disjunction) */
HOAConsumer::label_expr::ptr LabelExpr() {
HOAConsumer::label_expr::ptr left = LabelExprAnd();
while (token.kind == HOALexer::TOKEN_OR) {
expect(HOALexer::TOKEN_OR);
HOAConsumer::label_expr::ptr right = LabelExprAnd();
left = left | right;
}
return left;
}
/** Grammar rule for a label expression (handle conjunction) */
HOAConsumer::label_expr::ptr LabelExprAnd() {
HOAConsumer::label_expr::ptr left = LabelExprAtom();
while (token.kind == HOALexer::TOKEN_AND) {
expect(HOALexer::TOKEN_AND);
HOAConsumer::label_expr::ptr right = LabelExprAtom();
left = left & right;
}
return left;
}
/** Grammar rule for a label expression (handle atoms) */
HOAConsumer::label_expr::ptr LabelExprAtom() {
HOAConsumer::label_expr::ptr result;
switch (token.kind) {
case HOALexer::TOKEN_LPARENTH:
expect(HOALexer::TOKEN_LPARENTH);
result = LabelExpr();
expect(HOALexer::TOKEN_RPARENTH);
return result;
case HOALexer::TOKEN_TRUE:
expect(HOALexer::TOKEN_TRUE);
result.reset(new HOAConsumer::label_expr(true));
return result;
case HOALexer::TOKEN_FALSE:
expect(HOALexer::TOKEN_FALSE);
result.reset(new HOAConsumer::label_expr(false));
return result;
case HOALexer::TOKEN_NOT:
expect(HOALexer::TOKEN_NOT);
result = LabelExprAtom();
return !result;
case HOALexer::TOKEN_INT: {
unsigned int apIndex = Integer();
result.reset(new HOAConsumer::label_expr(AtomLabel::createAPIndex(apIndex)));
return result;
}
case HOALexer::TOKEN_ALIAS_NAME: {
std::string aliasName = AliasName();
result.reset(new HOAConsumer::label_expr(AtomLabel::createAlias(aliasName)));
return result;
}
default:
throw error("label expression");
}
}
/** Grammar rule for a quoted string */
std::string QuotedString(const std::string& context="") {
if (token.kind != HOALexer::TOKEN_STRING) {
expect(HOALexer::TOKEN_STRING, context);
}
std::string result = token.vString;
// eat token
nextToken();
result = HOAParserHelper::unquote(result);
return result;
}
/** Grammar rule for a HOA identifier */
std::string Identifier(const std::string& context="") {
if (token.kind != HOALexer::TOKEN_IDENT) {
expect(HOALexer::TOKEN_IDENT, context);
}
std::string result = token.vString;
// eat token
nextToken();
return result;
}
/** Grammar rule for an @@alias-name. Returns the name without the leading @@. */
std::string AliasName() {
if (token.kind != HOALexer::TOKEN_ALIAS_NAME) {
expect(HOALexer::TOKEN_ALIAS_NAME);
}
std::string result = token.vString;
// eat token
nextToken();
// eat @
result = result.substr(1);
return result;
}
/** Grammar rule for an unsigned integer */
unsigned int Integer(const std::string& context="") {
if (token.kind != HOALexer::TOKEN_INT) {
expect(HOALexer::TOKEN_INT, context);
}
unsigned int result = token.vInteger;
// eat token
nextToken();
return result;
}
};
}
#endif