|
|
//==============================================================================
//
// 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
|